Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
409
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/README.md
vendored
Normal file
409
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/README.md
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
# Stock Market Data Generation Examples
|
||||
|
||||
Comprehensive examples for generating realistic financial market data using agentic-synth. These examples are designed for testing trading systems, backtesting strategies, and financial analysis.
|
||||
|
||||
## Overview
|
||||
|
||||
This package provides three main categories of financial data generation:
|
||||
|
||||
1. **Market Data** (`market-data.ts`) - Time-series price data with technical indicators
|
||||
2. **Trading Scenarios** (`trading-scenarios.ts`) - Market regime simulations for system testing
|
||||
3. **Portfolio Simulation** (`portfolio-simulation.ts`) - Multi-asset portfolio management data
|
||||
|
||||
## Features
|
||||
|
||||
### Market Data Generation
|
||||
|
||||
Generate realistic market microstructure data including:
|
||||
|
||||
- **OHLCV Data**: Open, High, Low, Close, Volume candlestick bars
|
||||
- **Technical Indicators**: SMA, RSI, MACD, Bollinger Bands
|
||||
- **Multi-Timeframe**: 1m, 5m, 1h, 1d aggregation
|
||||
- **Market Depth**: Level 2 order book data
|
||||
- **Tick Data**: High-frequency tick-by-tick trades
|
||||
- **Microstructure Metrics**: Spreads, liquidity, toxicity
|
||||
|
||||
### Trading Scenarios
|
||||
|
||||
Realistic market conditions for testing trading systems:
|
||||
|
||||
- **Bull Markets**: Sustained uptrends with occasional pullbacks
|
||||
- **Bear Markets**: Downtrends with volatility spikes
|
||||
- **Volatility Regimes**: Low, medium, high, extreme volatility
|
||||
- **Flash Crashes**: Rapid price declines with recovery
|
||||
- **Earnings Events**: Announcement impact with IV crush
|
||||
- **Market Correlations**: Multi-asset correlation patterns
|
||||
|
||||
### Portfolio Simulation
|
||||
|
||||
Complete portfolio management workflow:
|
||||
|
||||
- **Multi-Asset Portfolios**: Diversified across asset classes
|
||||
- **Rebalancing**: Calendar, threshold, and opportunistic strategies
|
||||
- **Risk Metrics**: Sharpe, Sortino, Calmar, Information ratios
|
||||
- **Drawdown Analysis**: Peak-to-trough analysis with recovery
|
||||
- **Performance Attribution**: Alpha, beta, tracking error
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cd packages/agentic-synth
|
||||
npm install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Running Individual Examples
|
||||
|
||||
```bash
|
||||
# Market data generation
|
||||
npx ts-node examples/stocks/market-data.ts
|
||||
|
||||
# Trading scenarios
|
||||
npx ts-node examples/stocks/trading-scenarios.ts
|
||||
|
||||
# Portfolio simulation
|
||||
npx ts-node examples/stocks/portfolio-simulation.ts
|
||||
```
|
||||
|
||||
### Importing in Your Code
|
||||
|
||||
```typescript
|
||||
import {
|
||||
generateOHLCVData,
|
||||
generateTechnicalIndicators,
|
||||
generateMultiTimeframeData,
|
||||
} from './examples/stocks/market-data';
|
||||
|
||||
import {
|
||||
generateBullMarket,
|
||||
generateBearMarket,
|
||||
generateFlashCrash,
|
||||
} from './examples/stocks/trading-scenarios';
|
||||
|
||||
import {
|
||||
generateMultiAssetPortfolio,
|
||||
generateRebalancingScenarios,
|
||||
generateRiskAdjustedReturns,
|
||||
} from './examples/stocks/portfolio-simulation';
|
||||
|
||||
// Use in your application
|
||||
const ohlcvData = await generateOHLCVData();
|
||||
const bullMarket = await generateBullMarket();
|
||||
const portfolio = await generateMultiAssetPortfolio();
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### 1. OHLCV Data Generation
|
||||
|
||||
Generate realistic candlestick data with proper OHLCV relationships:
|
||||
|
||||
```typescript
|
||||
const ohlcvData = await generateOHLCVData();
|
||||
// Returns: Array of 390 1-minute bars for a trading day
|
||||
// Each bar: { timestamp, open, high, low, close, volume, symbol }
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- High >= max(open, close)
|
||||
- Low <= min(open, close)
|
||||
- Next bar opens at previous close
|
||||
- Realistic volume patterns
|
||||
|
||||
### 2. Technical Indicators
|
||||
|
||||
Calculate common technical indicators on price data:
|
||||
|
||||
```typescript
|
||||
const technicalData = await generateTechnicalIndicators();
|
||||
// Returns: Price data with SMA, RSI, MACD, Bollinger Bands
|
||||
```
|
||||
|
||||
**Indicators Included:**
|
||||
- SMA 20 & 50 (Simple Moving Averages)
|
||||
- RSI 14 (Relative Strength Index)
|
||||
- MACD & Signal Line
|
||||
- Bollinger Bands (upper, middle, lower)
|
||||
|
||||
### 3. Multi-Timeframe Data
|
||||
|
||||
Generate data across multiple timeframes with proper aggregation:
|
||||
|
||||
```typescript
|
||||
const multiTF = await generateMultiTimeframeData();
|
||||
// Returns: { '1m': [], '5m': [], '1h': [], '1d': [] }
|
||||
```
|
||||
|
||||
**Timeframes:**
|
||||
- 1-minute bars (base timeframe)
|
||||
- 5-minute bars (aggregated from 1m)
|
||||
- 1-hour bars (aggregated from 1m)
|
||||
- 1-day bars (aggregated from 1m)
|
||||
|
||||
### 4. Market Depth (Order Book)
|
||||
|
||||
Generate Level 2 market depth data:
|
||||
|
||||
```typescript
|
||||
const marketDepth = await generateMarketDepth();
|
||||
// Returns: Order book snapshots with bids/asks
|
||||
```
|
||||
|
||||
**Order Book Features:**
|
||||
- 20 levels on each side
|
||||
- Realistic size distribution
|
||||
- Order count per level
|
||||
- Spread and mid-price calculation
|
||||
|
||||
### 5. Bull Market Scenario
|
||||
|
||||
Simulate a sustained uptrend:
|
||||
|
||||
```typescript
|
||||
const bullMarket = await generateBullMarket();
|
||||
// Generates: 252 days of bull market with ~20% annual return
|
||||
```
|
||||
|
||||
**Characteristics:**
|
||||
- Upward drift with occasional pullbacks
|
||||
- Lower volatility
|
||||
- Volume increases on breakouts
|
||||
- Momentum indicators trend positive
|
||||
|
||||
### 6. Flash Crash Simulation
|
||||
|
||||
Model rapid price decline and recovery:
|
||||
|
||||
```typescript
|
||||
const flashCrash = await generateFlashCrash();
|
||||
// Phases: Normal → Crash (15% drop) → Recovery
|
||||
```
|
||||
|
||||
**Phases:**
|
||||
- **Normal**: Typical trading patterns
|
||||
- **Crash**: Exponential price decay, wide spreads, liquidity evaporation
|
||||
- **Recovery**: Quick rebound with reduced liquidity
|
||||
|
||||
### 7. Multi-Asset Portfolio
|
||||
|
||||
Create a diversified portfolio across asset classes:
|
||||
|
||||
```typescript
|
||||
const portfolio = await generateMultiAssetPortfolio();
|
||||
// Returns: { portfolioData, portfolioMetrics, assets }
|
||||
```
|
||||
|
||||
**Asset Allocation:**
|
||||
- 60% Equities (SPY, QQQ, IWM, EFA)
|
||||
- 30% Fixed Income (AGG, TLT)
|
||||
- 10% Alternatives (GLD, VNQ)
|
||||
|
||||
**Metrics Tracked:**
|
||||
- Total value and returns
|
||||
- Sharpe ratio
|
||||
- Maximum drawdown
|
||||
- Volatility
|
||||
- Alpha and beta
|
||||
|
||||
### 8. Rebalancing Scenarios
|
||||
|
||||
Simulate portfolio rebalancing strategies:
|
||||
|
||||
```typescript
|
||||
const rebalancing = await generateRebalancingScenarios();
|
||||
// Returns: Rebalance events with trades and costs
|
||||
```
|
||||
|
||||
**Rebalancing Types:**
|
||||
- **Calendar**: Quarterly (every 63 trading days)
|
||||
- **Threshold**: When drift exceeds 5%
|
||||
- **Opportunistic**: Based on market conditions
|
||||
|
||||
### 9. Drawdown Analysis
|
||||
|
||||
Comprehensive drawdown tracking and analysis:
|
||||
|
||||
```typescript
|
||||
const drawdowns = await generateDrawdownAnalysis();
|
||||
// Returns: All drawdown periods with recovery info
|
||||
```
|
||||
|
||||
**Drawdown Metrics:**
|
||||
- Maximum drawdown (peak to trough)
|
||||
- Drawdown duration
|
||||
- Recovery duration
|
||||
- Currently underwater status
|
||||
- Top 5 largest drawdowns
|
||||
|
||||
## Realistic Patterns
|
||||
|
||||
All generated data includes realistic market microstructure patterns:
|
||||
|
||||
### Price Dynamics
|
||||
- **Mean Reversion**: Prices tend to revert to moving averages
|
||||
- **Momentum**: Trends persist with gradual reversals
|
||||
- **Volatility Clustering**: Volatile periods cluster together
|
||||
- **Fat Tails**: Extreme moves occur more than normal distribution
|
||||
|
||||
### Volume Patterns
|
||||
- **Volume-Price Relationship**: Volume increases with volatility
|
||||
- **Institutional Activity**: Block trades and large orders
|
||||
- **Time-of-Day**: Higher volume at open and close
|
||||
- **Event-Driven**: Spikes during announcements
|
||||
|
||||
### Market Microstructure
|
||||
- **Bid-Ask Spread**: Realistic spread dynamics
|
||||
- **Market Impact**: Large orders move prices
|
||||
- **Liquidity**: Depth varies with market conditions
|
||||
- **Order Imbalance**: Buy/sell pressure affects prices
|
||||
|
||||
## Regulatory Compliance
|
||||
|
||||
All generated data follows regulatory standards:
|
||||
|
||||
### Trade Conditions
|
||||
- `BLOCK`: Large institutional trades (100+ shares)
|
||||
- `INSTITUTIONAL`: Very large orders (10,000+ shares)
|
||||
- `ODD_LOT`: Non-standard lot sizes
|
||||
- `EXTENDED_HOURS`: Pre-market and after-hours trades
|
||||
|
||||
### Data Quality
|
||||
- No negative prices or volumes
|
||||
- OHLCV relationships enforced
|
||||
- Realistic tick sizes (pennies)
|
||||
- Proper timestamp ordering
|
||||
|
||||
### Risk Disclosures
|
||||
⚠️ **IMPORTANT**: This is simulated data for testing purposes only. Do not use for:
|
||||
- Production trading decisions
|
||||
- Financial advice
|
||||
- Regulatory reporting
|
||||
- Real money trading without proper validation
|
||||
|
||||
## Performance
|
||||
|
||||
Generation performance for typical use cases:
|
||||
|
||||
| Dataset | Size | Generation Time |
|
||||
|---------|------|----------------|
|
||||
| 1-day OHLCV (1m) | 390 bars | ~50ms |
|
||||
| 1-year daily | 252 bars | ~30ms |
|
||||
| Tick data | 10,000 ticks | ~200ms |
|
||||
| Order book | 100 snapshots | ~150ms |
|
||||
| Multi-asset portfolio | 252 days | ~500ms |
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Custom Asset Classes
|
||||
|
||||
```typescript
|
||||
const customAssets: Asset[] = [
|
||||
{
|
||||
symbol: 'CUSTOM',
|
||||
assetClass: 'equity',
|
||||
weight: 0.50,
|
||||
expectedReturn: 0.15,
|
||||
volatility: 0.25,
|
||||
},
|
||||
// Add more assets...
|
||||
];
|
||||
```
|
||||
|
||||
### Custom Rebalancing Logic
|
||||
|
||||
```typescript
|
||||
const rebalanceThreshold = 0.10; // 10% drift
|
||||
const rebalanceFrequency = 21; // Monthly
|
||||
|
||||
// Implement custom rebalancing logic
|
||||
if (shouldRebalance(portfolio, threshold)) {
|
||||
await rebalance(portfolio, targetWeights);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Risk Metrics
|
||||
|
||||
```typescript
|
||||
// Calculate custom risk metrics
|
||||
const varCalc = (returns: number[], confidence: number) => {
|
||||
const sorted = returns.sort((a, b) => a - b);
|
||||
const index = Math.floor(returns.length * (1 - confidence));
|
||||
return sorted[index];
|
||||
};
|
||||
|
||||
const var95 = varCalc(returns, 0.95);
|
||||
const cvar95 = returns.filter(r => r <= var95).reduce((a, b) => a + b) / returns.length;
|
||||
```
|
||||
|
||||
## Testing Trading Systems
|
||||
|
||||
These examples are ideal for:
|
||||
|
||||
1. **Backtesting**: Test strategies against historical scenarios
|
||||
2. **Stress Testing**: Evaluate performance under extreme conditions
|
||||
3. **Risk Management**: Validate risk models and limits
|
||||
4. **Algorithm Development**: Develop and tune trading algorithms
|
||||
5. **Portfolio Optimization**: Test allocation strategies
|
||||
|
||||
### Example Backtest
|
||||
|
||||
```typescript
|
||||
// Generate test data
|
||||
const bullMarket = await generateBullMarket();
|
||||
const bearMarket = await generateBearMarket();
|
||||
const flashCrash = await generateFlashCrash();
|
||||
|
||||
// Test strategy on each scenario
|
||||
const results = {
|
||||
bull: await backtest(strategy, bullMarket),
|
||||
bear: await backtest(strategy, bearMarket),
|
||||
crash: await backtest(strategy, flashCrash),
|
||||
};
|
||||
|
||||
// Analyze results
|
||||
console.log('Strategy Performance:');
|
||||
console.log(`Bull Market: ${results.bull.return}%`);
|
||||
console.log(`Bear Market: ${results.bear.return}%`);
|
||||
console.log(`Flash Crash: ${results.crash.maxDrawdown}%`);
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Areas for improvement:
|
||||
|
||||
- [ ] Options pricing data
|
||||
- [ ] Futures and derivatives
|
||||
- [ ] Cryptocurrency markets
|
||||
- [ ] FX (foreign exchange) data
|
||||
- [ ] High-frequency market making scenarios
|
||||
- [ ] Credit spreads and fixed income
|
||||
- [ ] Alternative data integration
|
||||
|
||||
## Resources
|
||||
|
||||
### Financial Concepts
|
||||
- [Market Microstructure](https://en.wikipedia.org/wiki/Market_microstructure)
|
||||
- [Technical Analysis](https://www.investopedia.com/technical-analysis-4689657)
|
||||
- [Portfolio Theory](https://www.investopedia.com/terms/m/modernportfoliotheory.asp)
|
||||
- [Risk Metrics](https://www.investopedia.com/terms/r/riskadjustedreturn.asp)
|
||||
|
||||
### Trading System Development
|
||||
- [Quantitative Trading](https://www.quantstart.com/)
|
||||
- [Algorithmic Trading](https://www.algorithmictrading.net/)
|
||||
- [Backtesting Best Practices](https://www.quantconnect.com/docs/)
|
||||
|
||||
### Regulatory Guidelines
|
||||
- [SEC Trading Rules](https://www.sec.gov/fast-answers)
|
||||
- [FINRA Regulations](https://www.finra.org/rules-guidance)
|
||||
- [Market Data Standards](https://www.iso20022.org/)
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software is for educational and testing purposes only. The authors are not responsible for any financial losses incurred from using this software. Always consult with a qualified financial advisor before making investment decisions.
|
||||
|
||||
**Past performance does not guarantee future results.**
|
||||
61
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.d.ts
vendored
Normal file
61
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.d.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Stock Market Data Generation Examples
|
||||
*
|
||||
* Demonstrates realistic OHLCV data generation, technical indicators,
|
||||
* multi-timeframe data, market depth, and tick-by-tick simulation.
|
||||
*/
|
||||
interface OHLCVBar {
|
||||
timestamp: Date;
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
symbol: string;
|
||||
}
|
||||
/**
|
||||
* Generate realistic OHLCV (candlestick) data with proper market microstructure
|
||||
*/
|
||||
declare function generateOHLCVData(): Promise<any>;
|
||||
interface TechnicalIndicators {
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
sma_20: number;
|
||||
sma_50: number;
|
||||
rsi_14: number;
|
||||
macd: number;
|
||||
macd_signal: number;
|
||||
bb_upper: number;
|
||||
bb_middle: number;
|
||||
bb_lower: number;
|
||||
volume: number;
|
||||
symbol: string;
|
||||
}
|
||||
/**
|
||||
* Generate price data with technical indicators pre-calculated
|
||||
*/
|
||||
declare function generateTechnicalIndicators(): Promise<TechnicalIndicators[]>;
|
||||
interface MultiTimeframeData {
|
||||
'1m': OHLCVBar[];
|
||||
'5m': OHLCVBar[];
|
||||
'1h': OHLCVBar[];
|
||||
'1d': OHLCVBar[];
|
||||
}
|
||||
/**
|
||||
* Generate data across multiple timeframes with proper aggregation
|
||||
*/
|
||||
declare function generateMultiTimeframeData(): Promise<MultiTimeframeData>;
|
||||
/**
|
||||
* Generate realistic Level 2 market depth data (order book)
|
||||
*/
|
||||
declare function generateMarketDepth(): Promise<any>;
|
||||
/**
|
||||
* Generate high-frequency tick-by-tick trade data
|
||||
*/
|
||||
declare function generateTickData(): Promise<any>;
|
||||
/**
|
||||
* Generate market microstructure metrics for analysis
|
||||
*/
|
||||
declare function generateMicrostructureMetrics(): Promise<any>;
|
||||
export { generateOHLCVData, generateTechnicalIndicators, generateMultiTimeframeData, generateMarketDepth, generateTickData, generateMicrostructureMetrics, };
|
||||
//# sourceMappingURL=market-data.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"market-data.d.ts","sourceRoot":"","sources":["market-data.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,UAAU,QAAQ;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,iBAAe,iBAAiB,iBAmD/B;AAMD,UAAU,mBAAmB;IAC3B,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,iBAAe,2BAA2B,mCAwEzC;AAMD,UAAU,kBAAkB;IAC1B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,iBAAe,0BAA0B,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAgFvE;AAqBD;;GAEG;AACH,iBAAe,mBAAmB,iBAiEjC;AAgBD;;GAEG;AACH,iBAAe,gBAAgB,iBA+D9B;AAiBD;;GAEG;AACH,iBAAe,6BAA6B,iBA0B3C;AAiDD,OAAO,EACL,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,gBAAgB,EAChB,6BAA6B,GAC9B,CAAC"}
|
||||
404
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.js
vendored
Normal file
404
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.js
vendored
Normal file
@@ -0,0 +1,404 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Stock Market Data Generation Examples
|
||||
*
|
||||
* Demonstrates realistic OHLCV data generation, technical indicators,
|
||||
* multi-timeframe data, market depth, and tick-by-tick simulation.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.generateOHLCVData = generateOHLCVData;
|
||||
exports.generateTechnicalIndicators = generateTechnicalIndicators;
|
||||
exports.generateMultiTimeframeData = generateMultiTimeframeData;
|
||||
exports.generateMarketDepth = generateMarketDepth;
|
||||
exports.generateTickData = generateTickData;
|
||||
exports.generateMicrostructureMetrics = generateMicrostructureMetrics;
|
||||
const src_1 = require("../../../src");
|
||||
/**
|
||||
* Generate realistic OHLCV (candlestick) data with proper market microstructure
|
||||
*/
|
||||
async function generateOHLCVData() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const ohlcvData = await synth.generate({
|
||||
count: 390, // One trading day (6.5 hours * 60 minutes)
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
open: '{{number.float(100, 200, 2)}}',
|
||||
high: '{{number.float(100, 200, 2)}}',
|
||||
low: '{{number.float(100, 200, 2)}}',
|
||||
close: '{{number.float(100, 200, 2)}}',
|
||||
volume: '{{number.int(100000, 10000000)}}',
|
||||
symbol: 'AAPL',
|
||||
},
|
||||
constraints: [
|
||||
// High must be >= max(open, close)
|
||||
'bar.high >= Math.max(bar.open, bar.close)',
|
||||
// Low must be <= min(open, close)
|
||||
'bar.low <= Math.min(bar.open, bar.close)',
|
||||
// Volume must be positive
|
||||
'bar.volume > 0',
|
||||
],
|
||||
relationships: [
|
||||
{
|
||||
type: 'temporal',
|
||||
field: 'timestamp',
|
||||
interval: '1m', // 1-minute bars
|
||||
},
|
||||
{
|
||||
type: 'continuity',
|
||||
sourceField: 'close',
|
||||
targetField: 'open',
|
||||
description: 'Next bar opens at previous close',
|
||||
},
|
||||
],
|
||||
});
|
||||
// Post-process to ensure OHLCV validity
|
||||
const validatedData = ohlcvData.map((bar, idx) => {
|
||||
if (idx > 0) {
|
||||
bar.open = ohlcvData[idx - 1].close; // Open = previous close
|
||||
}
|
||||
bar.high = Math.max(bar.open, bar.close, bar.high);
|
||||
bar.low = Math.min(bar.open, bar.close, bar.low);
|
||||
return bar;
|
||||
});
|
||||
console.log('Generated OHLCV Data (first 5 bars):');
|
||||
console.log(validatedData.slice(0, 5));
|
||||
return validatedData;
|
||||
}
|
||||
/**
|
||||
* Generate price data with technical indicators pre-calculated
|
||||
*/
|
||||
async function generateTechnicalIndicators() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
// First generate base price series
|
||||
const priceData = await synth.generate({
|
||||
count: 100,
|
||||
template: {
|
||||
price: '{{number.float(150, 160, 2)}}',
|
||||
volume: '{{number.int(1000000, 5000000)}}',
|
||||
},
|
||||
});
|
||||
// Calculate indicators (simplified for demonstration)
|
||||
const calculateSMA = (data, period) => {
|
||||
const sma = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i < period - 1) {
|
||||
sma.push(data[i]);
|
||||
}
|
||||
else {
|
||||
const sum = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
|
||||
sma.push(sum / period);
|
||||
}
|
||||
}
|
||||
return sma;
|
||||
};
|
||||
const calculateRSI = (data, period = 14) => {
|
||||
const rsi = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i < period) {
|
||||
rsi.push(50); // Neutral RSI for initial period
|
||||
}
|
||||
else {
|
||||
const gains = [];
|
||||
const losses = [];
|
||||
for (let j = i - period + 1; j <= i; j++) {
|
||||
const change = data[j] - data[j - 1];
|
||||
if (change > 0)
|
||||
gains.push(change);
|
||||
else
|
||||
losses.push(Math.abs(change));
|
||||
}
|
||||
const avgGain = gains.reduce((a, b) => a + b, 0) / period;
|
||||
const avgLoss = losses.reduce((a, b) => a + b, 0) / period;
|
||||
const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;
|
||||
rsi.push(100 - 100 / (1 + rs));
|
||||
}
|
||||
}
|
||||
return rsi;
|
||||
};
|
||||
const prices = priceData.map((d) => d.price);
|
||||
const sma20 = calculateSMA(prices, 20);
|
||||
const sma50 = calculateSMA(prices, 50);
|
||||
const rsi = calculateRSI(prices);
|
||||
const technicalData = priceData.map((bar, idx) => ({
|
||||
timestamp: new Date(Date.now() - (priceData.length - idx) * 60000),
|
||||
price: bar.price,
|
||||
sma_20: Number(sma20[idx].toFixed(2)),
|
||||
sma_50: Number(sma50[idx].toFixed(2)),
|
||||
rsi_14: Number(rsi[idx].toFixed(2)),
|
||||
macd: Number((sma20[idx] - sma50[idx]).toFixed(2)),
|
||||
macd_signal: Number((sma20[idx] - sma50[idx]) * 0.9).toFixed(2), // Simplified
|
||||
bb_upper: Number((sma20[idx] * 1.02).toFixed(2)),
|
||||
bb_middle: Number(sma20[idx].toFixed(2)),
|
||||
bb_lower: Number((sma20[idx] * 0.98).toFixed(2)),
|
||||
volume: bar.volume,
|
||||
symbol: 'AAPL',
|
||||
}));
|
||||
console.log('Technical Indicators (last 5 bars):');
|
||||
console.log(technicalData.slice(-5));
|
||||
return technicalData;
|
||||
}
|
||||
/**
|
||||
* Generate data across multiple timeframes with proper aggregation
|
||||
*/
|
||||
async function generateMultiTimeframeData() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
// Generate 1-minute bars (base timeframe)
|
||||
const bars1m = await synth.generate({
|
||||
count: 1560, // 4 trading days worth of 1-minute data
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
open: '{{number.float(100, 200, 2)}}',
|
||||
high: '{{number.float(100, 200, 2)}}',
|
||||
low: '{{number.float(100, 200, 2)}}',
|
||||
close: '{{number.float(100, 200, 2)}}',
|
||||
volume: '{{number.int(10000, 100000)}}',
|
||||
symbol: 'AAPL',
|
||||
},
|
||||
});
|
||||
// Aggregate to 5-minute bars
|
||||
const bars5m = [];
|
||||
for (let i = 0; i < bars1m.length; i += 5) {
|
||||
const chunk = bars1m.slice(i, i + 5);
|
||||
if (chunk.length === 5) {
|
||||
bars5m.push({
|
||||
timestamp: chunk[0].timestamp,
|
||||
open: chunk[0].open,
|
||||
high: Math.max(...chunk.map((b) => b.high)),
|
||||
low: Math.min(...chunk.map((b) => b.low)),
|
||||
close: chunk[4].close,
|
||||
volume: chunk.reduce((sum, b) => sum + b.volume, 0),
|
||||
symbol: 'AAPL',
|
||||
});
|
||||
}
|
||||
}
|
||||
// Aggregate to 1-hour bars
|
||||
const bars1h = [];
|
||||
for (let i = 0; i < bars1m.length; i += 60) {
|
||||
const chunk = bars1m.slice(i, i + 60);
|
||||
if (chunk.length === 60) {
|
||||
bars1h.push({
|
||||
timestamp: chunk[0].timestamp,
|
||||
open: chunk[0].open,
|
||||
high: Math.max(...chunk.map((b) => b.high)),
|
||||
low: Math.min(...chunk.map((b) => b.low)),
|
||||
close: chunk[59].close,
|
||||
volume: chunk.reduce((sum, b) => sum + b.volume, 0),
|
||||
symbol: 'AAPL',
|
||||
});
|
||||
}
|
||||
}
|
||||
// Aggregate to 1-day bars
|
||||
const bars1d = [];
|
||||
for (let i = 0; i < bars1m.length; i += 390) {
|
||||
const chunk = bars1m.slice(i, i + 390);
|
||||
if (chunk.length === 390) {
|
||||
bars1d.push({
|
||||
timestamp: chunk[0].timestamp,
|
||||
open: chunk[0].open,
|
||||
high: Math.max(...chunk.map((b) => b.high)),
|
||||
low: Math.min(...chunk.map((b) => b.low)),
|
||||
close: chunk[389].close,
|
||||
volume: chunk.reduce((sum, b) => sum + b.volume, 0),
|
||||
symbol: 'AAPL',
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log('Multi-timeframe data generated:');
|
||||
console.log(`1m bars: ${bars1m.length}`);
|
||||
console.log(`5m bars: ${bars5m.length}`);
|
||||
console.log(`1h bars: ${bars1h.length}`);
|
||||
console.log(`1d bars: ${bars1d.length}`);
|
||||
return {
|
||||
'1m': bars1m,
|
||||
'5m': bars5m,
|
||||
'1h': bars1h,
|
||||
'1d': bars1d,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Generate realistic Level 2 market depth data (order book)
|
||||
*/
|
||||
async function generateMarketDepth() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const midPrice = 150.0;
|
||||
const tickSize = 0.01;
|
||||
const depth = 20; // 20 levels on each side
|
||||
const marketDepth = await synth.generate({
|
||||
count: 100,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
symbol: 'AAPL',
|
||||
bids: [],
|
||||
asks: [],
|
||||
spread: 0,
|
||||
midPrice: midPrice,
|
||||
},
|
||||
});
|
||||
// Generate order book levels
|
||||
const enrichedDepth = marketDepth.map((snapshot) => {
|
||||
const bids = [];
|
||||
const asks = [];
|
||||
// Generate bid side (below mid price)
|
||||
for (let i = 0; i < depth; i++) {
|
||||
bids.push({
|
||||
price: Number((midPrice - i * tickSize).toFixed(2)),
|
||||
size: Math.floor(Math.random() * 10000) + 100,
|
||||
orders: Math.floor(Math.random() * 50) + 1,
|
||||
});
|
||||
}
|
||||
// Generate ask side (above mid price)
|
||||
for (let i = 0; i < depth; i++) {
|
||||
asks.push({
|
||||
price: Number((midPrice + (i + 1) * tickSize).toFixed(2)),
|
||||
size: Math.floor(Math.random() * 10000) + 100,
|
||||
orders: Math.floor(Math.random() * 50) + 1,
|
||||
});
|
||||
}
|
||||
const bestBid = bids[0].price;
|
||||
const bestAsk = asks[0].price;
|
||||
return {
|
||||
...snapshot,
|
||||
bids,
|
||||
asks,
|
||||
spread: Number((bestAsk - bestBid).toFixed(2)),
|
||||
midPrice: Number(((bestBid + bestAsk) / 2).toFixed(2)),
|
||||
};
|
||||
});
|
||||
console.log('Market Depth (first snapshot):');
|
||||
console.log({
|
||||
timestamp: enrichedDepth[0].timestamp,
|
||||
bestBid: enrichedDepth[0].bids[0],
|
||||
bestAsk: enrichedDepth[0].asks[0],
|
||||
spread: enrichedDepth[0].spread,
|
||||
totalBidVolume: enrichedDepth[0].bids.reduce((sum, b) => sum + b.size, 0),
|
||||
totalAskVolume: enrichedDepth[0].asks.reduce((sum, a) => sum + a.size, 0),
|
||||
});
|
||||
return enrichedDepth;
|
||||
}
|
||||
/**
|
||||
* Generate high-frequency tick-by-tick trade data
|
||||
*/
|
||||
async function generateTickData() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const tickData = await synth.generate({
|
||||
count: 10000, // 10k ticks (typical for a few minutes of active trading)
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
symbol: 'AAPL',
|
||||
price: '{{number.float(149.5, 150.5, 2)}}',
|
||||
size: '{{number.int(1, 1000)}}',
|
||||
side: '{{random.arrayElement(["buy", "sell"])}}',
|
||||
exchange: '{{random.arrayElement(["NASDAQ", "NYSE", "BATS", "IEX"])}}',
|
||||
conditions: [],
|
||||
},
|
||||
constraints: [
|
||||
'tick.size > 0',
|
||||
'tick.price > 0',
|
||||
],
|
||||
relationships: [
|
||||
{
|
||||
type: 'temporal',
|
||||
field: 'timestamp',
|
||||
interval: '10ms', // High-frequency ticks
|
||||
},
|
||||
],
|
||||
});
|
||||
// Add trade conditions (regulatory tags)
|
||||
const enrichedTicks = tickData.map((tick) => {
|
||||
const conditions = [];
|
||||
if (tick.size >= 100)
|
||||
conditions.push('BLOCK');
|
||||
if (tick.size >= 10000)
|
||||
conditions.push('INSTITUTIONAL');
|
||||
if (Math.random() < 0.05)
|
||||
conditions.push('ODD_LOT');
|
||||
if (Math.random() < 0.1)
|
||||
conditions.push('EXTENDED_HOURS');
|
||||
return {
|
||||
...tick,
|
||||
conditions,
|
||||
};
|
||||
});
|
||||
// Calculate tick statistics
|
||||
const buyTicks = enrichedTicks.filter((t) => t.side === 'buy');
|
||||
const sellTicks = enrichedTicks.filter((t) => t.side === 'sell');
|
||||
const avgBuyPrice = buyTicks.reduce((sum, t) => sum + t.price, 0) / buyTicks.length;
|
||||
const avgSellPrice = sellTicks.reduce((sum, t) => sum + t.price, 0) / sellTicks.length;
|
||||
console.log('Tick Data Statistics:');
|
||||
console.log({
|
||||
totalTicks: enrichedTicks.length,
|
||||
buyTicks: buyTicks.length,
|
||||
sellTicks: sellTicks.length,
|
||||
avgBuyPrice: avgBuyPrice.toFixed(2),
|
||||
avgSellPrice: avgSellPrice.toFixed(2),
|
||||
priceImbalance: (avgBuyPrice - avgSellPrice).toFixed(4),
|
||||
avgTradeSize: enrichedTicks.reduce((sum, t) => sum + t.size, 0) / enrichedTicks.length,
|
||||
});
|
||||
return enrichedTicks;
|
||||
}
|
||||
/**
|
||||
* Generate market microstructure metrics for analysis
|
||||
*/
|
||||
async function generateMicrostructureMetrics() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const metrics = await synth.generate({
|
||||
count: 390, // One trading day
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
symbol: 'AAPL',
|
||||
effectiveSpread: '{{number.float(0.01, 0.05, 4)}}',
|
||||
realizedSpread: '{{number.float(0.005, 0.03, 4)}}',
|
||||
priceImpact: '{{number.float(0.001, 0.02, 4)}}',
|
||||
toxicity: '{{number.float(0, 1, 4)}}',
|
||||
orderImbalance: '{{number.float(-1, 1, 4)}}',
|
||||
volatility: '{{number.float(0.01, 0.05, 4)}}',
|
||||
},
|
||||
constraints: [
|
||||
'metrics.effectiveSpread >= metrics.realizedSpread',
|
||||
'metrics.toxicity >= 0 && metrics.toxicity <= 1',
|
||||
'metrics.orderImbalance >= -1 && metrics.orderImbalance <= 1',
|
||||
],
|
||||
});
|
||||
console.log('Microstructure Metrics (sample):');
|
||||
console.log(metrics.slice(0, 5));
|
||||
return metrics;
|
||||
}
|
||||
// ============================================================================
|
||||
// Main Execution
|
||||
// ============================================================================
|
||||
async function main() {
|
||||
console.log('='.repeat(80));
|
||||
console.log('Stock Market Data Generation Examples');
|
||||
console.log('='.repeat(80));
|
||||
console.log();
|
||||
try {
|
||||
console.log('1. Generating OHLCV Data...');
|
||||
await generateOHLCVData();
|
||||
console.log();
|
||||
console.log('2. Generating Technical Indicators...');
|
||||
await generateTechnicalIndicators();
|
||||
console.log();
|
||||
console.log('3. Generating Multi-Timeframe Data...');
|
||||
await generateMultiTimeframeData();
|
||||
console.log();
|
||||
console.log('4. Generating Market Depth...');
|
||||
await generateMarketDepth();
|
||||
console.log();
|
||||
console.log('5. Generating Tick Data...');
|
||||
await generateTickData();
|
||||
console.log();
|
||||
console.log('6. Generating Microstructure Metrics...');
|
||||
await generateMicrostructureMetrics();
|
||||
console.log();
|
||||
console.log('All examples completed successfully!');
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error generating market data:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
// Run if executed directly
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
//# sourceMappingURL=market-data.js.map
|
||||
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
543
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.ts
vendored
Normal file
543
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/market-data.ts
vendored
Normal file
@@ -0,0 +1,543 @@
|
||||
/**
|
||||
* Stock Market Data Generation Examples
|
||||
*
|
||||
* Demonstrates realistic OHLCV data generation, technical indicators,
|
||||
* multi-timeframe data, market depth, and tick-by-tick simulation.
|
||||
*/
|
||||
|
||||
import { AgenticSynth } from '../../../src';
|
||||
|
||||
// ============================================================================
|
||||
// OHLCV Data Generation
|
||||
// ============================================================================
|
||||
|
||||
interface OHLCVBar {
|
||||
timestamp: Date;
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate realistic OHLCV (candlestick) data with proper market microstructure
|
||||
*/
|
||||
async function generateOHLCVData() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const ohlcvData = await synth.generate<OHLCVBar>({
|
||||
count: 390, // One trading day (6.5 hours * 60 minutes)
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
open: '{{number.float(100, 200, 2)}}',
|
||||
high: '{{number.float(100, 200, 2)}}',
|
||||
low: '{{number.float(100, 200, 2)}}',
|
||||
close: '{{number.float(100, 200, 2)}}',
|
||||
volume: '{{number.int(100000, 10000000)}}',
|
||||
symbol: 'AAPL',
|
||||
},
|
||||
constraints: [
|
||||
// High must be >= max(open, close)
|
||||
'bar.high >= Math.max(bar.open, bar.close)',
|
||||
// Low must be <= min(open, close)
|
||||
'bar.low <= Math.min(bar.open, bar.close)',
|
||||
// Volume must be positive
|
||||
'bar.volume > 0',
|
||||
],
|
||||
relationships: [
|
||||
{
|
||||
type: 'temporal',
|
||||
field: 'timestamp',
|
||||
interval: '1m', // 1-minute bars
|
||||
},
|
||||
{
|
||||
type: 'continuity',
|
||||
sourceField: 'close',
|
||||
targetField: 'open',
|
||||
description: 'Next bar opens at previous close',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Post-process to ensure OHLCV validity
|
||||
const validatedData = ohlcvData.map((bar, idx) => {
|
||||
if (idx > 0) {
|
||||
bar.open = ohlcvData[idx - 1].close; // Open = previous close
|
||||
}
|
||||
bar.high = Math.max(bar.open, bar.close, bar.high);
|
||||
bar.low = Math.min(bar.open, bar.close, bar.low);
|
||||
return bar;
|
||||
});
|
||||
|
||||
console.log('Generated OHLCV Data (first 5 bars):');
|
||||
console.log(validatedData.slice(0, 5));
|
||||
|
||||
return validatedData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Technical Indicators
|
||||
// ============================================================================
|
||||
|
||||
interface TechnicalIndicators {
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
sma_20: number;
|
||||
sma_50: number;
|
||||
rsi_14: number;
|
||||
macd: number;
|
||||
macd_signal: number;
|
||||
bb_upper: number;
|
||||
bb_middle: number;
|
||||
bb_lower: number;
|
||||
volume: number;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate price data with technical indicators pre-calculated
|
||||
*/
|
||||
async function generateTechnicalIndicators() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
// First generate base price series
|
||||
const priceData = await synth.generate<{ price: number; volume: number }>({
|
||||
count: 100,
|
||||
template: {
|
||||
price: '{{number.float(150, 160, 2)}}',
|
||||
volume: '{{number.int(1000000, 5000000)}}',
|
||||
},
|
||||
});
|
||||
|
||||
// Calculate indicators (simplified for demonstration)
|
||||
const calculateSMA = (data: number[], period: number): number[] => {
|
||||
const sma: number[] = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i < period - 1) {
|
||||
sma.push(data[i]);
|
||||
} else {
|
||||
const sum = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
|
||||
sma.push(sum / period);
|
||||
}
|
||||
}
|
||||
return sma;
|
||||
};
|
||||
|
||||
const calculateRSI = (data: number[], period: number = 14): number[] => {
|
||||
const rsi: number[] = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i < period) {
|
||||
rsi.push(50); // Neutral RSI for initial period
|
||||
} else {
|
||||
const gains: number[] = [];
|
||||
const losses: number[] = [];
|
||||
for (let j = i - period + 1; j <= i; j++) {
|
||||
const change = data[j] - data[j - 1];
|
||||
if (change > 0) gains.push(change);
|
||||
else losses.push(Math.abs(change));
|
||||
}
|
||||
const avgGain = gains.reduce((a, b) => a + b, 0) / period;
|
||||
const avgLoss = losses.reduce((a, b) => a + b, 0) / period;
|
||||
const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;
|
||||
rsi.push(100 - 100 / (1 + rs));
|
||||
}
|
||||
}
|
||||
return rsi;
|
||||
};
|
||||
|
||||
const prices = priceData.map((d) => d.price);
|
||||
const sma20 = calculateSMA(prices, 20);
|
||||
const sma50 = calculateSMA(prices, 50);
|
||||
const rsi = calculateRSI(prices);
|
||||
|
||||
const technicalData: TechnicalIndicators[] = priceData.map((bar, idx) => ({
|
||||
timestamp: new Date(Date.now() - (priceData.length - idx) * 60000),
|
||||
price: bar.price,
|
||||
sma_20: Number(sma20[idx].toFixed(2)),
|
||||
sma_50: Number(sma50[idx].toFixed(2)),
|
||||
rsi_14: Number(rsi[idx].toFixed(2)),
|
||||
macd: Number((sma20[idx] - sma50[idx]).toFixed(2)),
|
||||
macd_signal: Number((sma20[idx] - sma50[idx]) * 0.9).toFixed(2), // Simplified
|
||||
bb_upper: Number((sma20[idx] * 1.02).toFixed(2)),
|
||||
bb_middle: Number(sma20[idx].toFixed(2)),
|
||||
bb_lower: Number((sma20[idx] * 0.98).toFixed(2)),
|
||||
volume: bar.volume,
|
||||
symbol: 'AAPL',
|
||||
}));
|
||||
|
||||
console.log('Technical Indicators (last 5 bars):');
|
||||
console.log(technicalData.slice(-5));
|
||||
|
||||
return technicalData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Multi-Timeframe Data
|
||||
// ============================================================================
|
||||
|
||||
interface MultiTimeframeData {
|
||||
'1m': OHLCVBar[];
|
||||
'5m': OHLCVBar[];
|
||||
'1h': OHLCVBar[];
|
||||
'1d': OHLCVBar[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate data across multiple timeframes with proper aggregation
|
||||
*/
|
||||
async function generateMultiTimeframeData(): Promise<MultiTimeframeData> {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
// Generate 1-minute bars (base timeframe)
|
||||
const bars1m = await synth.generate<OHLCVBar>({
|
||||
count: 1560, // 4 trading days worth of 1-minute data
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
open: '{{number.float(100, 200, 2)}}',
|
||||
high: '{{number.float(100, 200, 2)}}',
|
||||
low: '{{number.float(100, 200, 2)}}',
|
||||
close: '{{number.float(100, 200, 2)}}',
|
||||
volume: '{{number.int(10000, 100000)}}',
|
||||
symbol: 'AAPL',
|
||||
},
|
||||
});
|
||||
|
||||
// Aggregate to 5-minute bars
|
||||
const bars5m: OHLCVBar[] = [];
|
||||
for (let i = 0; i < bars1m.length; i += 5) {
|
||||
const chunk = bars1m.slice(i, i + 5);
|
||||
if (chunk.length === 5) {
|
||||
bars5m.push({
|
||||
timestamp: chunk[0].timestamp,
|
||||
open: chunk[0].open,
|
||||
high: Math.max(...chunk.map((b) => b.high)),
|
||||
low: Math.min(...chunk.map((b) => b.low)),
|
||||
close: chunk[4].close,
|
||||
volume: chunk.reduce((sum, b) => sum + b.volume, 0),
|
||||
symbol: 'AAPL',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate to 1-hour bars
|
||||
const bars1h: OHLCVBar[] = [];
|
||||
for (let i = 0; i < bars1m.length; i += 60) {
|
||||
const chunk = bars1m.slice(i, i + 60);
|
||||
if (chunk.length === 60) {
|
||||
bars1h.push({
|
||||
timestamp: chunk[0].timestamp,
|
||||
open: chunk[0].open,
|
||||
high: Math.max(...chunk.map((b) => b.high)),
|
||||
low: Math.min(...chunk.map((b) => b.low)),
|
||||
close: chunk[59].close,
|
||||
volume: chunk.reduce((sum, b) => sum + b.volume, 0),
|
||||
symbol: 'AAPL',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate to 1-day bars
|
||||
const bars1d: OHLCVBar[] = [];
|
||||
for (let i = 0; i < bars1m.length; i += 390) {
|
||||
const chunk = bars1m.slice(i, i + 390);
|
||||
if (chunk.length === 390) {
|
||||
bars1d.push({
|
||||
timestamp: chunk[0].timestamp,
|
||||
open: chunk[0].open,
|
||||
high: Math.max(...chunk.map((b) => b.high)),
|
||||
low: Math.min(...chunk.map((b) => b.low)),
|
||||
close: chunk[389].close,
|
||||
volume: chunk.reduce((sum, b) => sum + b.volume, 0),
|
||||
symbol: 'AAPL',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Multi-timeframe data generated:');
|
||||
console.log(`1m bars: ${bars1m.length}`);
|
||||
console.log(`5m bars: ${bars5m.length}`);
|
||||
console.log(`1h bars: ${bars1h.length}`);
|
||||
console.log(`1d bars: ${bars1d.length}`);
|
||||
|
||||
return {
|
||||
'1m': bars1m,
|
||||
'5m': bars5m,
|
||||
'1h': bars1h,
|
||||
'1d': bars1d,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Market Depth Data (Order Book)
|
||||
// ============================================================================
|
||||
|
||||
interface OrderBookLevel {
|
||||
price: number;
|
||||
size: number;
|
||||
orders: number;
|
||||
}
|
||||
|
||||
interface MarketDepth {
|
||||
timestamp: Date;
|
||||
symbol: string;
|
||||
bids: OrderBookLevel[];
|
||||
asks: OrderBookLevel[];
|
||||
spread: number;
|
||||
midPrice: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate realistic Level 2 market depth data (order book)
|
||||
*/
|
||||
async function generateMarketDepth() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const midPrice = 150.0;
|
||||
const tickSize = 0.01;
|
||||
const depth = 20; // 20 levels on each side
|
||||
|
||||
const marketDepth = await synth.generate<MarketDepth>({
|
||||
count: 100,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
symbol: 'AAPL',
|
||||
bids: [],
|
||||
asks: [],
|
||||
spread: 0,
|
||||
midPrice: midPrice,
|
||||
},
|
||||
});
|
||||
|
||||
// Generate order book levels
|
||||
const enrichedDepth = marketDepth.map((snapshot) => {
|
||||
const bids: OrderBookLevel[] = [];
|
||||
const asks: OrderBookLevel[] = [];
|
||||
|
||||
// Generate bid side (below mid price)
|
||||
for (let i = 0; i < depth; i++) {
|
||||
bids.push({
|
||||
price: Number((midPrice - i * tickSize).toFixed(2)),
|
||||
size: Math.floor(Math.random() * 10000) + 100,
|
||||
orders: Math.floor(Math.random() * 50) + 1,
|
||||
});
|
||||
}
|
||||
|
||||
// Generate ask side (above mid price)
|
||||
for (let i = 0; i < depth; i++) {
|
||||
asks.push({
|
||||
price: Number((midPrice + (i + 1) * tickSize).toFixed(2)),
|
||||
size: Math.floor(Math.random() * 10000) + 100,
|
||||
orders: Math.floor(Math.random() * 50) + 1,
|
||||
});
|
||||
}
|
||||
|
||||
const bestBid = bids[0].price;
|
||||
const bestAsk = asks[0].price;
|
||||
|
||||
return {
|
||||
...snapshot,
|
||||
bids,
|
||||
asks,
|
||||
spread: Number((bestAsk - bestBid).toFixed(2)),
|
||||
midPrice: Number(((bestBid + bestAsk) / 2).toFixed(2)),
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Market Depth (first snapshot):');
|
||||
console.log({
|
||||
timestamp: enrichedDepth[0].timestamp,
|
||||
bestBid: enrichedDepth[0].bids[0],
|
||||
bestAsk: enrichedDepth[0].asks[0],
|
||||
spread: enrichedDepth[0].spread,
|
||||
totalBidVolume: enrichedDepth[0].bids.reduce((sum, b) => sum + b.size, 0),
|
||||
totalAskVolume: enrichedDepth[0].asks.reduce((sum, a) => sum + a.size, 0),
|
||||
});
|
||||
|
||||
return enrichedDepth;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tick-by-Tick Data
|
||||
// ============================================================================
|
||||
|
||||
interface Tick {
|
||||
timestamp: Date;
|
||||
symbol: string;
|
||||
price: number;
|
||||
size: number;
|
||||
side: 'buy' | 'sell';
|
||||
exchange: string;
|
||||
conditions: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate high-frequency tick-by-tick trade data
|
||||
*/
|
||||
async function generateTickData() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const tickData = await synth.generate<Tick>({
|
||||
count: 10000, // 10k ticks (typical for a few minutes of active trading)
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
symbol: 'AAPL',
|
||||
price: '{{number.float(149.5, 150.5, 2)}}',
|
||||
size: '{{number.int(1, 1000)}}',
|
||||
side: '{{random.arrayElement(["buy", "sell"])}}',
|
||||
exchange: '{{random.arrayElement(["NASDAQ", "NYSE", "BATS", "IEX"])}}',
|
||||
conditions: [],
|
||||
},
|
||||
constraints: [
|
||||
'tick.size > 0',
|
||||
'tick.price > 0',
|
||||
],
|
||||
relationships: [
|
||||
{
|
||||
type: 'temporal',
|
||||
field: 'timestamp',
|
||||
interval: '10ms', // High-frequency ticks
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Add trade conditions (regulatory tags)
|
||||
const enrichedTicks = tickData.map((tick) => {
|
||||
const conditions: string[] = [];
|
||||
|
||||
if (tick.size >= 100) conditions.push('BLOCK');
|
||||
if (tick.size >= 10000) conditions.push('INSTITUTIONAL');
|
||||
if (Math.random() < 0.05) conditions.push('ODD_LOT');
|
||||
if (Math.random() < 0.1) conditions.push('EXTENDED_HOURS');
|
||||
|
||||
return {
|
||||
...tick,
|
||||
conditions,
|
||||
};
|
||||
});
|
||||
|
||||
// Calculate tick statistics
|
||||
const buyTicks = enrichedTicks.filter((t) => t.side === 'buy');
|
||||
const sellTicks = enrichedTicks.filter((t) => t.side === 'sell');
|
||||
const avgBuyPrice =
|
||||
buyTicks.reduce((sum, t) => sum + t.price, 0) / buyTicks.length;
|
||||
const avgSellPrice =
|
||||
sellTicks.reduce((sum, t) => sum + t.price, 0) / sellTicks.length;
|
||||
|
||||
console.log('Tick Data Statistics:');
|
||||
console.log({
|
||||
totalTicks: enrichedTicks.length,
|
||||
buyTicks: buyTicks.length,
|
||||
sellTicks: sellTicks.length,
|
||||
avgBuyPrice: avgBuyPrice.toFixed(2),
|
||||
avgSellPrice: avgSellPrice.toFixed(2),
|
||||
priceImbalance: (avgBuyPrice - avgSellPrice).toFixed(4),
|
||||
avgTradeSize:
|
||||
enrichedTicks.reduce((sum, t) => sum + t.size, 0) / enrichedTicks.length,
|
||||
});
|
||||
|
||||
return enrichedTicks;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Market Microstructure Patterns
|
||||
// ============================================================================
|
||||
|
||||
interface MicrostructureMetrics {
|
||||
timestamp: Date;
|
||||
symbol: string;
|
||||
effectiveSpread: number;
|
||||
realizedSpread: number;
|
||||
priceImpact: number;
|
||||
toxicity: number; // Adverse selection measure
|
||||
orderImbalance: number;
|
||||
volatility: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate market microstructure metrics for analysis
|
||||
*/
|
||||
async function generateMicrostructureMetrics() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const metrics = await synth.generate<MicrostructureMetrics>({
|
||||
count: 390, // One trading day
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
symbol: 'AAPL',
|
||||
effectiveSpread: '{{number.float(0.01, 0.05, 4)}}',
|
||||
realizedSpread: '{{number.float(0.005, 0.03, 4)}}',
|
||||
priceImpact: '{{number.float(0.001, 0.02, 4)}}',
|
||||
toxicity: '{{number.float(0, 1, 4)}}',
|
||||
orderImbalance: '{{number.float(-1, 1, 4)}}',
|
||||
volatility: '{{number.float(0.01, 0.05, 4)}}',
|
||||
},
|
||||
constraints: [
|
||||
'metrics.effectiveSpread >= metrics.realizedSpread',
|
||||
'metrics.toxicity >= 0 && metrics.toxicity <= 1',
|
||||
'metrics.orderImbalance >= -1 && metrics.orderImbalance <= 1',
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Microstructure Metrics (sample):');
|
||||
console.log(metrics.slice(0, 5));
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main Execution
|
||||
// ============================================================================
|
||||
|
||||
async function main() {
|
||||
console.log('='.repeat(80));
|
||||
console.log('Stock Market Data Generation Examples');
|
||||
console.log('='.repeat(80));
|
||||
console.log();
|
||||
|
||||
try {
|
||||
console.log('1. Generating OHLCV Data...');
|
||||
await generateOHLCVData();
|
||||
console.log();
|
||||
|
||||
console.log('2. Generating Technical Indicators...');
|
||||
await generateTechnicalIndicators();
|
||||
console.log();
|
||||
|
||||
console.log('3. Generating Multi-Timeframe Data...');
|
||||
await generateMultiTimeframeData();
|
||||
console.log();
|
||||
|
||||
console.log('4. Generating Market Depth...');
|
||||
await generateMarketDepth();
|
||||
console.log();
|
||||
|
||||
console.log('5. Generating Tick Data...');
|
||||
await generateTickData();
|
||||
console.log();
|
||||
|
||||
console.log('6. Generating Microstructure Metrics...');
|
||||
await generateMicrostructureMetrics();
|
||||
console.log();
|
||||
|
||||
console.log('All examples completed successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error generating market data:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
export {
|
||||
generateOHLCVData,
|
||||
generateTechnicalIndicators,
|
||||
generateMultiTimeframeData,
|
||||
generateMarketDepth,
|
||||
generateTickData,
|
||||
generateMicrostructureMetrics,
|
||||
};
|
||||
104
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.d.ts
vendored
Normal file
104
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.d.ts
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Portfolio Simulation and Management
|
||||
*
|
||||
* Generate realistic portfolio data for:
|
||||
* - Multi-asset portfolios
|
||||
* - Rebalancing scenarios
|
||||
* - Risk-adjusted returns
|
||||
* - Drawdown analysis
|
||||
* - Performance attribution
|
||||
*/
|
||||
interface Asset {
|
||||
symbol: string;
|
||||
assetClass: 'equity' | 'fixedIncome' | 'commodity' | 'crypto' | 'alternative';
|
||||
weight: number;
|
||||
expectedReturn: number;
|
||||
volatility: number;
|
||||
}
|
||||
interface PortfolioHolding {
|
||||
timestamp: Date;
|
||||
symbol: string;
|
||||
shares: number;
|
||||
price: number;
|
||||
marketValue: number;
|
||||
weight: number;
|
||||
dayReturn: number;
|
||||
totalReturn: number;
|
||||
}
|
||||
interface PortfolioMetrics {
|
||||
timestamp: Date;
|
||||
totalValue: number;
|
||||
cashBalance: number;
|
||||
totalReturn: number;
|
||||
dailyReturn: number;
|
||||
volatility: number;
|
||||
sharpeRatio: number;
|
||||
maxDrawdown: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
}
|
||||
/**
|
||||
* Generate a diversified multi-asset portfolio
|
||||
*/
|
||||
declare function generateMultiAssetPortfolio(): Promise<{
|
||||
portfolioData: Map<string, PortfolioHolding[]>;
|
||||
portfolioMetrics: PortfolioMetrics[];
|
||||
assets: Asset[];
|
||||
}>;
|
||||
interface RebalanceEvent {
|
||||
timestamp: Date;
|
||||
type: 'calendar' | 'threshold' | 'opportunistic';
|
||||
holdings: PortfolioHolding[];
|
||||
targetWeights: Record<string, number>;
|
||||
actualWeights: Record<string, number>;
|
||||
trades: Trade[];
|
||||
transactionCosts: number;
|
||||
}
|
||||
interface Trade {
|
||||
symbol: string;
|
||||
action: 'buy' | 'sell';
|
||||
shares: number;
|
||||
price: number;
|
||||
value: number;
|
||||
commission: number;
|
||||
}
|
||||
/**
|
||||
* Generate portfolio rebalancing scenarios
|
||||
*/
|
||||
declare function generateRebalancingScenarios(): Promise<RebalanceEvent[]>;
|
||||
interface RiskMetrics {
|
||||
timestamp: Date;
|
||||
portfolioReturn: number;
|
||||
benchmarkReturn: number;
|
||||
excessReturn: number;
|
||||
trackingError: number;
|
||||
informationRatio: number;
|
||||
sharpeRatio: number;
|
||||
sortinoRatio: number;
|
||||
calmarRatio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
correlation: number;
|
||||
}
|
||||
/**
|
||||
* Calculate comprehensive risk-adjusted return metrics
|
||||
*/
|
||||
declare function generateRiskAdjustedReturns(): Promise<RiskMetrics[]>;
|
||||
interface DrawdownPeriod {
|
||||
startDate: Date;
|
||||
troughDate: Date;
|
||||
endDate: Date | null;
|
||||
peakValue: number;
|
||||
troughValue: number;
|
||||
recoveryValue: number | null;
|
||||
drawdown: number;
|
||||
duration: number;
|
||||
recoveryDuration: number | null;
|
||||
underwater: boolean;
|
||||
}
|
||||
/**
|
||||
* Analyze portfolio drawdowns
|
||||
*/
|
||||
declare function generateDrawdownAnalysis(): Promise<DrawdownPeriod[]>;
|
||||
export { generateMultiAssetPortfolio, generateRebalancingScenarios, generateRiskAdjustedReturns, generateDrawdownAnalysis, };
|
||||
//# sourceMappingURL=portfolio-simulation.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"portfolio-simulation.d.ts","sourceRoot":"","sources":["portfolio-simulation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,QAAQ,GAAG,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,CAAC;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,gBAAgB;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,gBAAgB;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;GAEG;AACH,iBAAe,2BAA2B;;;;GAqIzC;AAMD,UAAU,cAAc;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,eAAe,CAAC;IACjD,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,iBAAe,4BAA4B,8BAkG1C;AAMD,UAAU,WAAW;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,iBAAe,2BAA2B,2BAmGzC;AAMD,UAAU,cAAc;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,iBAAe,wBAAwB,8BA0FtC;AAwCD,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,GACzB,CAAC"}
|
||||
395
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.js
vendored
Normal file
395
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.js
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Portfolio Simulation and Management
|
||||
*
|
||||
* Generate realistic portfolio data for:
|
||||
* - Multi-asset portfolios
|
||||
* - Rebalancing scenarios
|
||||
* - Risk-adjusted returns
|
||||
* - Drawdown analysis
|
||||
* - Performance attribution
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.generateMultiAssetPortfolio = generateMultiAssetPortfolio;
|
||||
exports.generateRebalancingScenarios = generateRebalancingScenarios;
|
||||
exports.generateRiskAdjustedReturns = generateRiskAdjustedReturns;
|
||||
exports.generateDrawdownAnalysis = generateDrawdownAnalysis;
|
||||
const src_1 = require("../../../src");
|
||||
// ============================================================================
|
||||
// Multi-Asset Portfolio Generation
|
||||
// ============================================================================
|
||||
/**
|
||||
* Generate a diversified multi-asset portfolio
|
||||
*/
|
||||
async function generateMultiAssetPortfolio() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
// Define portfolio composition
|
||||
const assets = [
|
||||
// Equities (60%)
|
||||
{ symbol: 'SPY', assetClass: 'equity', weight: 0.30, expectedReturn: 0.10, volatility: 0.15 },
|
||||
{ symbol: 'QQQ', assetClass: 'equity', weight: 0.15, expectedReturn: 0.12, volatility: 0.20 },
|
||||
{ symbol: 'IWM', assetClass: 'equity', weight: 0.10, expectedReturn: 0.11, volatility: 0.18 },
|
||||
{ symbol: 'EFA', assetClass: 'equity', weight: 0.05, expectedReturn: 0.08, volatility: 0.16 },
|
||||
// Fixed Income (30%)
|
||||
{ symbol: 'AGG', assetClass: 'fixedIncome', weight: 0.20, expectedReturn: 0.04, volatility: 0.05 },
|
||||
{ symbol: 'TLT', assetClass: 'fixedIncome', weight: 0.10, expectedReturn: 0.03, volatility: 0.12 },
|
||||
// Alternatives (10%)
|
||||
{ symbol: 'GLD', assetClass: 'commodity', weight: 0.05, expectedReturn: 0.05, volatility: 0.15 },
|
||||
{ symbol: 'VNQ', assetClass: 'alternative', weight: 0.05, expectedReturn: 0.08, volatility: 0.17 },
|
||||
];
|
||||
const days = 252; // One year
|
||||
const initialValue = 1000000; // $1M portfolio
|
||||
// Generate price series for each asset
|
||||
const portfolioData = new Map();
|
||||
for (const asset of assets) {
|
||||
const initialPrice = 100;
|
||||
let currentPrice = initialPrice;
|
||||
const shares = (initialValue * asset.weight) / initialPrice;
|
||||
const holdings = [];
|
||||
for (let day = 0; day < days; day++) {
|
||||
// Generate correlated returns
|
||||
const marketReturn = (Math.random() - 0.5) * 0.02;
|
||||
const idiosyncraticReturn = (Math.random() - 0.5) * asset.volatility * 0.5;
|
||||
const dailyReturn = marketReturn * 0.7 + idiosyncraticReturn + asset.expectedReturn / 252;
|
||||
currentPrice *= 1 + dailyReturn;
|
||||
const marketValue = shares * currentPrice;
|
||||
const totalReturn = (currentPrice - initialPrice) / initialPrice;
|
||||
holdings.push({
|
||||
timestamp: new Date(Date.now() - (days - day) * 86400000),
|
||||
symbol: asset.symbol,
|
||||
shares,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
marketValue: Number(marketValue.toFixed(2)),
|
||||
weight: asset.weight, // Will be updated later
|
||||
dayReturn: Number(dailyReturn.toFixed(6)),
|
||||
totalReturn: Number(totalReturn.toFixed(6)),
|
||||
});
|
||||
}
|
||||
portfolioData.set(asset.symbol, holdings);
|
||||
}
|
||||
// Calculate portfolio-level metrics
|
||||
const portfolioMetrics = [];
|
||||
for (let day = 0; day < days; day++) {
|
||||
let totalValue = 0;
|
||||
let dailyReturn = 0;
|
||||
assets.forEach((asset) => {
|
||||
const holding = portfolioData.get(asset.symbol)[day];
|
||||
totalValue += holding.marketValue;
|
||||
dailyReturn += holding.dayReturn * asset.weight;
|
||||
});
|
||||
// Update weights based on current market values
|
||||
assets.forEach((asset) => {
|
||||
const holding = portfolioData.get(asset.symbol)[day];
|
||||
holding.weight = holding.marketValue / totalValue;
|
||||
});
|
||||
// Calculate metrics
|
||||
const returns = Array.from({ length: Math.min(day + 1, 60) }, (_, i) => {
|
||||
let ret = 0;
|
||||
assets.forEach((asset) => {
|
||||
ret += portfolioData.get(asset.symbol)[day - i]?.dayReturn || 0;
|
||||
});
|
||||
return ret;
|
||||
});
|
||||
const volatility = Math.sqrt(returns.reduce((sum, r) => sum + Math.pow(r, 2), 0) / returns.length) * Math.sqrt(252);
|
||||
const totalReturn = (totalValue - initialValue) / initialValue;
|
||||
const sharpeRatio = (totalReturn * 252) / (volatility + 0.0001);
|
||||
// Calculate max drawdown
|
||||
const portfolioValues = Array.from({ length: day + 1 }, (_, i) => {
|
||||
let val = 0;
|
||||
assets.forEach((asset) => {
|
||||
val += portfolioData.get(asset.symbol)[i].marketValue;
|
||||
});
|
||||
return val;
|
||||
});
|
||||
const maxDrawdown = portfolioValues.reduce((maxDD, val, i) => {
|
||||
const peak = Math.max(...portfolioValues.slice(0, i + 1));
|
||||
const dd = (val - peak) / peak;
|
||||
return Math.min(maxDD, dd);
|
||||
}, 0);
|
||||
portfolioMetrics.push({
|
||||
timestamp: new Date(Date.now() - (days - day) * 86400000),
|
||||
totalValue: Number(totalValue.toFixed(2)),
|
||||
cashBalance: 0,
|
||||
totalReturn: Number(totalReturn.toFixed(6)),
|
||||
dailyReturn: Number(dailyReturn.toFixed(6)),
|
||||
volatility: Number(volatility.toFixed(4)),
|
||||
sharpeRatio: Number(sharpeRatio.toFixed(2)),
|
||||
maxDrawdown: Number(maxDrawdown.toFixed(4)),
|
||||
beta: 0.95, // Simplified
|
||||
alpha: Number(((totalReturn - 0.08 * (day / 252)) / (day / 252 + 0.0001)).toFixed(4)),
|
||||
});
|
||||
}
|
||||
console.log('Multi-Asset Portfolio:');
|
||||
console.log({
|
||||
initialValue,
|
||||
finalValue: portfolioMetrics[portfolioMetrics.length - 1].totalValue,
|
||||
totalReturn: (portfolioMetrics[portfolioMetrics.length - 1].totalReturn * 100).toFixed(2) + '%',
|
||||
sharpeRatio: portfolioMetrics[portfolioMetrics.length - 1].sharpeRatio,
|
||||
maxDrawdown: (portfolioMetrics[portfolioMetrics.length - 1].maxDrawdown * 100).toFixed(2) + '%',
|
||||
volatility: (portfolioMetrics[portfolioMetrics.length - 1].volatility * 100).toFixed(2) + '%',
|
||||
});
|
||||
return { portfolioData, portfolioMetrics, assets };
|
||||
}
|
||||
/**
|
||||
* Generate portfolio rebalancing scenarios
|
||||
*/
|
||||
async function generateRebalancingScenarios() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
// Start with portfolio from previous function
|
||||
const { portfolioData, assets } = await generateMultiAssetPortfolio();
|
||||
const rebalanceEvents = [];
|
||||
const rebalanceThreshold = 0.05; // Rebalance if drift > 5%
|
||||
const rebalanceFrequency = 63; // Quarterly (every 63 trading days)
|
||||
const days = 252;
|
||||
for (let day = 0; day < days; day++) {
|
||||
const currentHoldings = [];
|
||||
let totalValue = 0;
|
||||
// Get current holdings
|
||||
assets.forEach((asset) => {
|
||||
const holding = portfolioData.get(asset.symbol)[day];
|
||||
currentHoldings.push(holding);
|
||||
totalValue += holding.marketValue;
|
||||
});
|
||||
// Calculate current weights
|
||||
const actualWeights = {};
|
||||
currentHoldings.forEach((holding) => {
|
||||
actualWeights[holding.symbol] = holding.marketValue / totalValue;
|
||||
});
|
||||
// Check if rebalancing is needed
|
||||
const targetWeights = {};
|
||||
assets.forEach((asset) => {
|
||||
targetWeights[asset.symbol] = asset.weight;
|
||||
});
|
||||
let maxDrift = 0;
|
||||
Object.keys(targetWeights).forEach((symbol) => {
|
||||
const drift = Math.abs(actualWeights[symbol] - targetWeights[symbol]);
|
||||
maxDrift = Math.max(maxDrift, drift);
|
||||
});
|
||||
const shouldRebalance = maxDrift > rebalanceThreshold || day % rebalanceFrequency === 0;
|
||||
if (shouldRebalance) {
|
||||
const trades = [];
|
||||
let transactionCosts = 0;
|
||||
// Generate trades to rebalance
|
||||
Object.keys(targetWeights).forEach((symbol) => {
|
||||
const currentWeight = actualWeights[symbol];
|
||||
const targetWeight = targetWeights[symbol];
|
||||
const targetValue = totalValue * targetWeight;
|
||||
const currentValue = totalValue * currentWeight;
|
||||
const deltaValue = targetValue - currentValue;
|
||||
const holding = currentHoldings.find((h) => h.symbol === symbol);
|
||||
const deltaShares = deltaValue / holding.price;
|
||||
if (Math.abs(deltaShares) > 1) {
|
||||
const commission = Math.abs(deltaValue) * 0.0005; // 5 bps
|
||||
transactionCosts += commission;
|
||||
trades.push({
|
||||
symbol,
|
||||
action: deltaShares > 0 ? 'buy' : 'sell',
|
||||
shares: Math.abs(Math.floor(deltaShares)),
|
||||
price: holding.price,
|
||||
value: Math.abs(deltaValue),
|
||||
commission,
|
||||
});
|
||||
}
|
||||
});
|
||||
rebalanceEvents.push({
|
||||
timestamp: new Date(Date.now() - (days - day) * 86400000),
|
||||
type: maxDrift > rebalanceThreshold * 2 ? 'threshold' : 'calendar',
|
||||
holdings: currentHoldings,
|
||||
targetWeights,
|
||||
actualWeights,
|
||||
trades,
|
||||
transactionCosts,
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log('Rebalancing Scenarios:');
|
||||
console.log({
|
||||
totalRebalances: rebalanceEvents.length,
|
||||
avgTransactionCosts: rebalanceEvents.reduce((sum, e) => sum + e.transactionCosts, 0) /
|
||||
rebalanceEvents.length,
|
||||
totalTransactionCosts: rebalanceEvents.reduce((sum, e) => sum + e.transactionCosts, 0),
|
||||
});
|
||||
return rebalanceEvents;
|
||||
}
|
||||
/**
|
||||
* Calculate comprehensive risk-adjusted return metrics
|
||||
*/
|
||||
async function generateRiskAdjustedReturns() {
|
||||
const { portfolioMetrics } = await generateMultiAssetPortfolio();
|
||||
const riskFreeRate = 0.04; // 4% annual
|
||||
const dailyRfRate = riskFreeRate / 252;
|
||||
const riskMetrics = portfolioMetrics.map((metrics, idx) => {
|
||||
// Simulate benchmark (S&P 500)
|
||||
const benchmarkReturn = metrics.dailyReturn * 0.95 + (Math.random() - 0.5) * 0.005;
|
||||
const excessReturn = metrics.dailyReturn - dailyRfRate;
|
||||
// Calculate rolling metrics
|
||||
const window = Math.min(60, idx + 1);
|
||||
const recentReturns = portfolioMetrics
|
||||
.slice(Math.max(0, idx - window), idx + 1)
|
||||
.map((m) => m.dailyReturn);
|
||||
const recentBenchmarkReturns = Array.from({ length: window }, (_, i) => portfolioMetrics[Math.max(0, idx - window + i)].dailyReturn * 0.95);
|
||||
// Tracking error
|
||||
const trackingDiffs = recentReturns.map((r, i) => r - recentBenchmarkReturns[i]);
|
||||
const trackingError = Math.sqrt(trackingDiffs.reduce((sum, d) => sum + Math.pow(d, 2), 0) / window) * Math.sqrt(252);
|
||||
// Information ratio
|
||||
const avgExcessReturn = trackingDiffs.reduce((sum, d) => sum + d, 0) / window;
|
||||
const informationRatio = (avgExcessReturn * 252) / (trackingError + 0.0001);
|
||||
// Sortino ratio (downside deviation)
|
||||
const downsideReturns = recentReturns.filter((r) => r < dailyRfRate);
|
||||
const downsideDeviation = downsideReturns.length > 0
|
||||
? Math.sqrt(downsideReturns.reduce((sum, r) => sum + Math.pow(r - dailyRfRate, 2), 0) / downsideReturns.length) * Math.sqrt(252)
|
||||
: 0.0001;
|
||||
const avgReturn = recentReturns.reduce((sum, r) => sum + r, 0) / window;
|
||||
const sortinoRatio = ((avgReturn - dailyRfRate) * 252) / downsideDeviation;
|
||||
// Calmar ratio
|
||||
const calmarRatio = (avgReturn * 252) / (Math.abs(metrics.maxDrawdown) + 0.0001);
|
||||
// Beta and alpha
|
||||
const benchmarkVar = recentBenchmarkReturns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / window;
|
||||
const covariance = recentReturns.reduce((sum, r, i) => sum + (r - avgReturn) * (recentBenchmarkReturns[i] - avgReturn), 0) / window;
|
||||
const beta = covariance / (benchmarkVar + 0.0001);
|
||||
const alpha = (avgReturn - dailyRfRate - beta * (avgReturn * 0.95 - dailyRfRate)) * 252;
|
||||
// Correlation
|
||||
const correlation = covariance / (Math.sqrt(benchmarkVar) * metrics.volatility / Math.sqrt(252) + 0.0001);
|
||||
return {
|
||||
timestamp: metrics.timestamp,
|
||||
portfolioReturn: metrics.dailyReturn,
|
||||
benchmarkReturn,
|
||||
excessReturn,
|
||||
trackingError: Number(trackingError.toFixed(4)),
|
||||
informationRatio: Number(informationRatio.toFixed(2)),
|
||||
sharpeRatio: metrics.sharpeRatio,
|
||||
sortinoRatio: Number(sortinoRatio.toFixed(2)),
|
||||
calmarRatio: Number(calmarRatio.toFixed(2)),
|
||||
beta: Number(beta.toFixed(2)),
|
||||
alpha: Number(alpha.toFixed(4)),
|
||||
correlation: Number(correlation.toFixed(2)),
|
||||
};
|
||||
});
|
||||
console.log('Risk-Adjusted Returns (final metrics):');
|
||||
const finalMetrics = riskMetrics[riskMetrics.length - 1];
|
||||
console.log({
|
||||
sharpeRatio: finalMetrics.sharpeRatio,
|
||||
sortinoRatio: finalMetrics.sortinoRatio,
|
||||
calmarRatio: finalMetrics.calmarRatio,
|
||||
informationRatio: finalMetrics.informationRatio,
|
||||
beta: finalMetrics.beta,
|
||||
alpha: (finalMetrics.alpha * 100).toFixed(2) + '%',
|
||||
correlation: finalMetrics.correlation,
|
||||
});
|
||||
return riskMetrics;
|
||||
}
|
||||
/**
|
||||
* Analyze portfolio drawdowns
|
||||
*/
|
||||
async function generateDrawdownAnalysis() {
|
||||
const { portfolioMetrics } = await generateMultiAssetPortfolio();
|
||||
const drawdowns = [];
|
||||
let currentPeak = portfolioMetrics[0].totalValue;
|
||||
let currentPeakDate = portfolioMetrics[0].timestamp;
|
||||
let inDrawdown = false;
|
||||
let troughValue = currentPeak;
|
||||
let troughDate = currentPeakDate;
|
||||
let startDate = currentPeakDate;
|
||||
portfolioMetrics.forEach((metrics, idx) => {
|
||||
if (metrics.totalValue > currentPeak) {
|
||||
// New peak
|
||||
if (inDrawdown) {
|
||||
// End of drawdown - recovery complete
|
||||
drawdowns[drawdowns.length - 1].endDate = metrics.timestamp;
|
||||
drawdowns[drawdowns.length - 1].recoveryValue = metrics.totalValue;
|
||||
drawdowns[drawdowns.length - 1].recoveryDuration =
|
||||
(metrics.timestamp.getTime() - troughDate.getTime()) / 86400000;
|
||||
drawdowns[drawdowns.length - 1].underwater = false;
|
||||
inDrawdown = false;
|
||||
}
|
||||
currentPeak = metrics.totalValue;
|
||||
currentPeakDate = metrics.timestamp;
|
||||
}
|
||||
else if (metrics.totalValue < currentPeak) {
|
||||
// In drawdown
|
||||
if (!inDrawdown) {
|
||||
// Start of new drawdown
|
||||
startDate = currentPeakDate;
|
||||
troughValue = metrics.totalValue;
|
||||
troughDate = metrics.timestamp;
|
||||
inDrawdown = true;
|
||||
}
|
||||
if (metrics.totalValue < troughValue) {
|
||||
troughValue = metrics.totalValue;
|
||||
troughDate = metrics.timestamp;
|
||||
}
|
||||
// Update or create drawdown record
|
||||
const dd = (metrics.totalValue - currentPeak) / currentPeak;
|
||||
const duration = (troughDate.getTime() - startDate.getTime()) / 86400000;
|
||||
if (drawdowns.length === 0 || !drawdowns[drawdowns.length - 1].underwater) {
|
||||
drawdowns.push({
|
||||
startDate,
|
||||
troughDate,
|
||||
endDate: null,
|
||||
peakValue: currentPeak,
|
||||
troughValue,
|
||||
recoveryValue: null,
|
||||
drawdown: dd,
|
||||
duration,
|
||||
recoveryDuration: null,
|
||||
underwater: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
drawdowns[drawdowns.length - 1].troughDate = troughDate;
|
||||
drawdowns[drawdowns.length - 1].troughValue = troughValue;
|
||||
drawdowns[drawdowns.length - 1].drawdown = dd;
|
||||
drawdowns[drawdowns.length - 1].duration = duration;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Sort by drawdown magnitude
|
||||
const sortedDrawdowns = drawdowns.sort((a, b) => a.drawdown - b.drawdown);
|
||||
console.log('Drawdown Analysis:');
|
||||
console.log({
|
||||
totalDrawdowns: drawdowns.length,
|
||||
maxDrawdown: (sortedDrawdowns[0].drawdown * 100).toFixed(2) + '%',
|
||||
avgDrawdown: ((drawdowns.reduce((sum, dd) => sum + dd.drawdown, 0) / drawdowns.length) *
|
||||
100).toFixed(2) + '%',
|
||||
longestDrawdown: Math.max(...drawdowns.map((dd) => dd.duration)),
|
||||
currentlyUnderwater: drawdowns[drawdowns.length - 1]?.underwater || false,
|
||||
});
|
||||
console.log('\nTop 5 Drawdowns:');
|
||||
sortedDrawdowns.slice(0, 5).forEach((dd, idx) => {
|
||||
console.log(`${idx + 1}. ${(dd.drawdown * 100).toFixed(2)}% over ${dd.duration} days, ` +
|
||||
`recovered in ${dd.recoveryDuration || 'N/A'} days`);
|
||||
});
|
||||
return drawdowns;
|
||||
}
|
||||
// ============================================================================
|
||||
// Main Execution
|
||||
// ============================================================================
|
||||
async function main() {
|
||||
console.log('='.repeat(80));
|
||||
console.log('Portfolio Simulation and Management');
|
||||
console.log('='.repeat(80));
|
||||
console.log();
|
||||
try {
|
||||
console.log('1. Generating Multi-Asset Portfolio...');
|
||||
await generateMultiAssetPortfolio();
|
||||
console.log();
|
||||
console.log('2. Generating Rebalancing Scenarios...');
|
||||
await generateRebalancingScenarios();
|
||||
console.log();
|
||||
console.log('3. Calculating Risk-Adjusted Returns...');
|
||||
await generateRiskAdjustedReturns();
|
||||
console.log();
|
||||
console.log('4. Analyzing Drawdowns...');
|
||||
await generateDrawdownAnalysis();
|
||||
console.log();
|
||||
console.log('All portfolio simulations completed successfully!');
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error generating portfolio simulations:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
//# sourceMappingURL=portfolio-simulation.js.map
|
||||
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
596
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.ts
vendored
Normal file
596
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/portfolio-simulation.ts
vendored
Normal file
@@ -0,0 +1,596 @@
|
||||
/**
|
||||
* Portfolio Simulation and Management
|
||||
*
|
||||
* Generate realistic portfolio data for:
|
||||
* - Multi-asset portfolios
|
||||
* - Rebalancing scenarios
|
||||
* - Risk-adjusted returns
|
||||
* - Drawdown analysis
|
||||
* - Performance attribution
|
||||
*/
|
||||
|
||||
import { AgenticSynth } from '../../../src';
|
||||
|
||||
// ============================================================================
|
||||
// Portfolio Types and Interfaces
|
||||
// ============================================================================
|
||||
|
||||
interface Asset {
|
||||
symbol: string;
|
||||
assetClass: 'equity' | 'fixedIncome' | 'commodity' | 'crypto' | 'alternative';
|
||||
weight: number;
|
||||
expectedReturn: number;
|
||||
volatility: number;
|
||||
}
|
||||
|
||||
interface PortfolioHolding {
|
||||
timestamp: Date;
|
||||
symbol: string;
|
||||
shares: number;
|
||||
price: number;
|
||||
marketValue: number;
|
||||
weight: number;
|
||||
dayReturn: number;
|
||||
totalReturn: number;
|
||||
}
|
||||
|
||||
interface PortfolioMetrics {
|
||||
timestamp: Date;
|
||||
totalValue: number;
|
||||
cashBalance: number;
|
||||
totalReturn: number;
|
||||
dailyReturn: number;
|
||||
volatility: number;
|
||||
sharpeRatio: number;
|
||||
maxDrawdown: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Multi-Asset Portfolio Generation
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Generate a diversified multi-asset portfolio
|
||||
*/
|
||||
async function generateMultiAssetPortfolio() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
// Define portfolio composition
|
||||
const assets: Asset[] = [
|
||||
// Equities (60%)
|
||||
{ symbol: 'SPY', assetClass: 'equity', weight: 0.30, expectedReturn: 0.10, volatility: 0.15 },
|
||||
{ symbol: 'QQQ', assetClass: 'equity', weight: 0.15, expectedReturn: 0.12, volatility: 0.20 },
|
||||
{ symbol: 'IWM', assetClass: 'equity', weight: 0.10, expectedReturn: 0.11, volatility: 0.18 },
|
||||
{ symbol: 'EFA', assetClass: 'equity', weight: 0.05, expectedReturn: 0.08, volatility: 0.16 },
|
||||
|
||||
// Fixed Income (30%)
|
||||
{ symbol: 'AGG', assetClass: 'fixedIncome', weight: 0.20, expectedReturn: 0.04, volatility: 0.05 },
|
||||
{ symbol: 'TLT', assetClass: 'fixedIncome', weight: 0.10, expectedReturn: 0.03, volatility: 0.12 },
|
||||
|
||||
// Alternatives (10%)
|
||||
{ symbol: 'GLD', assetClass: 'commodity', weight: 0.05, expectedReturn: 0.05, volatility: 0.15 },
|
||||
{ symbol: 'VNQ', assetClass: 'alternative', weight: 0.05, expectedReturn: 0.08, volatility: 0.17 },
|
||||
];
|
||||
|
||||
const days = 252; // One year
|
||||
const initialValue = 1000000; // $1M portfolio
|
||||
|
||||
// Generate price series for each asset
|
||||
const portfolioData: Map<string, PortfolioHolding[]> = new Map();
|
||||
|
||||
for (const asset of assets) {
|
||||
const initialPrice = 100;
|
||||
let currentPrice = initialPrice;
|
||||
const shares = (initialValue * asset.weight) / initialPrice;
|
||||
|
||||
const holdings: PortfolioHolding[] = [];
|
||||
|
||||
for (let day = 0; day < days; day++) {
|
||||
// Generate correlated returns
|
||||
const marketReturn = (Math.random() - 0.5) * 0.02;
|
||||
const idiosyncraticReturn = (Math.random() - 0.5) * asset.volatility * 0.5;
|
||||
const dailyReturn = marketReturn * 0.7 + idiosyncraticReturn + asset.expectedReturn / 252;
|
||||
|
||||
currentPrice *= 1 + dailyReturn;
|
||||
const marketValue = shares * currentPrice;
|
||||
const totalReturn = (currentPrice - initialPrice) / initialPrice;
|
||||
|
||||
holdings.push({
|
||||
timestamp: new Date(Date.now() - (days - day) * 86400000),
|
||||
symbol: asset.symbol,
|
||||
shares,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
marketValue: Number(marketValue.toFixed(2)),
|
||||
weight: asset.weight, // Will be updated later
|
||||
dayReturn: Number(dailyReturn.toFixed(6)),
|
||||
totalReturn: Number(totalReturn.toFixed(6)),
|
||||
});
|
||||
}
|
||||
|
||||
portfolioData.set(asset.symbol, holdings);
|
||||
}
|
||||
|
||||
// Calculate portfolio-level metrics
|
||||
const portfolioMetrics: PortfolioMetrics[] = [];
|
||||
|
||||
for (let day = 0; day < days; day++) {
|
||||
let totalValue = 0;
|
||||
let dailyReturn = 0;
|
||||
|
||||
assets.forEach((asset) => {
|
||||
const holding = portfolioData.get(asset.symbol)![day];
|
||||
totalValue += holding.marketValue;
|
||||
dailyReturn += holding.dayReturn * asset.weight;
|
||||
});
|
||||
|
||||
// Update weights based on current market values
|
||||
assets.forEach((asset) => {
|
||||
const holding = portfolioData.get(asset.symbol)![day];
|
||||
holding.weight = holding.marketValue / totalValue;
|
||||
});
|
||||
|
||||
// Calculate metrics
|
||||
const returns = Array.from({ length: Math.min(day + 1, 60) }, (_, i) => {
|
||||
let ret = 0;
|
||||
assets.forEach((asset) => {
|
||||
ret += portfolioData.get(asset.symbol)![day - i]?.dayReturn || 0;
|
||||
});
|
||||
return ret;
|
||||
});
|
||||
|
||||
const volatility = Math.sqrt(
|
||||
returns.reduce((sum, r) => sum + Math.pow(r, 2), 0) / returns.length
|
||||
) * Math.sqrt(252);
|
||||
|
||||
const totalReturn = (totalValue - initialValue) / initialValue;
|
||||
const sharpeRatio = (totalReturn * 252) / (volatility + 0.0001);
|
||||
|
||||
// Calculate max drawdown
|
||||
const portfolioValues = Array.from({ length: day + 1 }, (_, i) => {
|
||||
let val = 0;
|
||||
assets.forEach((asset) => {
|
||||
val += portfolioData.get(asset.symbol)![i].marketValue;
|
||||
});
|
||||
return val;
|
||||
});
|
||||
|
||||
const maxDrawdown = portfolioValues.reduce((maxDD, val, i) => {
|
||||
const peak = Math.max(...portfolioValues.slice(0, i + 1));
|
||||
const dd = (val - peak) / peak;
|
||||
return Math.min(maxDD, dd);
|
||||
}, 0);
|
||||
|
||||
portfolioMetrics.push({
|
||||
timestamp: new Date(Date.now() - (days - day) * 86400000),
|
||||
totalValue: Number(totalValue.toFixed(2)),
|
||||
cashBalance: 0,
|
||||
totalReturn: Number(totalReturn.toFixed(6)),
|
||||
dailyReturn: Number(dailyReturn.toFixed(6)),
|
||||
volatility: Number(volatility.toFixed(4)),
|
||||
sharpeRatio: Number(sharpeRatio.toFixed(2)),
|
||||
maxDrawdown: Number(maxDrawdown.toFixed(4)),
|
||||
beta: 0.95, // Simplified
|
||||
alpha: Number(((totalReturn - 0.08 * (day / 252)) / (day / 252 + 0.0001)).toFixed(4)),
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Multi-Asset Portfolio:');
|
||||
console.log({
|
||||
initialValue,
|
||||
finalValue: portfolioMetrics[portfolioMetrics.length - 1].totalValue,
|
||||
totalReturn: (portfolioMetrics[portfolioMetrics.length - 1].totalReturn * 100).toFixed(2) + '%',
|
||||
sharpeRatio: portfolioMetrics[portfolioMetrics.length - 1].sharpeRatio,
|
||||
maxDrawdown: (portfolioMetrics[portfolioMetrics.length - 1].maxDrawdown * 100).toFixed(2) + '%',
|
||||
volatility: (portfolioMetrics[portfolioMetrics.length - 1].volatility * 100).toFixed(2) + '%',
|
||||
});
|
||||
|
||||
return { portfolioData, portfolioMetrics, assets };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Rebalancing Scenarios
|
||||
// ============================================================================
|
||||
|
||||
interface RebalanceEvent {
|
||||
timestamp: Date;
|
||||
type: 'calendar' | 'threshold' | 'opportunistic';
|
||||
holdings: PortfolioHolding[];
|
||||
targetWeights: Record<string, number>;
|
||||
actualWeights: Record<string, number>;
|
||||
trades: Trade[];
|
||||
transactionCosts: number;
|
||||
}
|
||||
|
||||
interface Trade {
|
||||
symbol: string;
|
||||
action: 'buy' | 'sell';
|
||||
shares: number;
|
||||
price: number;
|
||||
value: number;
|
||||
commission: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate portfolio rebalancing scenarios
|
||||
*/
|
||||
async function generateRebalancingScenarios() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
// Start with portfolio from previous function
|
||||
const { portfolioData, assets } = await generateMultiAssetPortfolio();
|
||||
|
||||
const rebalanceEvents: RebalanceEvent[] = [];
|
||||
const rebalanceThreshold = 0.05; // Rebalance if drift > 5%
|
||||
const rebalanceFrequency = 63; // Quarterly (every 63 trading days)
|
||||
|
||||
const days = 252;
|
||||
for (let day = 0; day < days; day++) {
|
||||
const currentHoldings: PortfolioHolding[] = [];
|
||||
let totalValue = 0;
|
||||
|
||||
// Get current holdings
|
||||
assets.forEach((asset) => {
|
||||
const holding = portfolioData.get(asset.symbol)![day];
|
||||
currentHoldings.push(holding);
|
||||
totalValue += holding.marketValue;
|
||||
});
|
||||
|
||||
// Calculate current weights
|
||||
const actualWeights: Record<string, number> = {};
|
||||
currentHoldings.forEach((holding) => {
|
||||
actualWeights[holding.symbol] = holding.marketValue / totalValue;
|
||||
});
|
||||
|
||||
// Check if rebalancing is needed
|
||||
const targetWeights: Record<string, number> = {};
|
||||
assets.forEach((asset) => {
|
||||
targetWeights[asset.symbol] = asset.weight;
|
||||
});
|
||||
|
||||
let maxDrift = 0;
|
||||
Object.keys(targetWeights).forEach((symbol) => {
|
||||
const drift = Math.abs(actualWeights[symbol] - targetWeights[symbol]);
|
||||
maxDrift = Math.max(maxDrift, drift);
|
||||
});
|
||||
|
||||
const shouldRebalance =
|
||||
maxDrift > rebalanceThreshold || day % rebalanceFrequency === 0;
|
||||
|
||||
if (shouldRebalance) {
|
||||
const trades: Trade[] = [];
|
||||
let transactionCosts = 0;
|
||||
|
||||
// Generate trades to rebalance
|
||||
Object.keys(targetWeights).forEach((symbol) => {
|
||||
const currentWeight = actualWeights[symbol];
|
||||
const targetWeight = targetWeights[symbol];
|
||||
const targetValue = totalValue * targetWeight;
|
||||
const currentValue = totalValue * currentWeight;
|
||||
const deltaValue = targetValue - currentValue;
|
||||
|
||||
const holding = currentHoldings.find((h) => h.symbol === symbol)!;
|
||||
const deltaShares = deltaValue / holding.price;
|
||||
|
||||
if (Math.abs(deltaShares) > 1) {
|
||||
const commission = Math.abs(deltaValue) * 0.0005; // 5 bps
|
||||
transactionCosts += commission;
|
||||
|
||||
trades.push({
|
||||
symbol,
|
||||
action: deltaShares > 0 ? 'buy' : 'sell',
|
||||
shares: Math.abs(Math.floor(deltaShares)),
|
||||
price: holding.price,
|
||||
value: Math.abs(deltaValue),
|
||||
commission,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rebalanceEvents.push({
|
||||
timestamp: new Date(Date.now() - (days - day) * 86400000),
|
||||
type: maxDrift > rebalanceThreshold * 2 ? 'threshold' : 'calendar',
|
||||
holdings: currentHoldings,
|
||||
targetWeights,
|
||||
actualWeights,
|
||||
trades,
|
||||
transactionCosts,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Rebalancing Scenarios:');
|
||||
console.log({
|
||||
totalRebalances: rebalanceEvents.length,
|
||||
avgTransactionCosts:
|
||||
rebalanceEvents.reduce((sum, e) => sum + e.transactionCosts, 0) /
|
||||
rebalanceEvents.length,
|
||||
totalTransactionCosts: rebalanceEvents.reduce(
|
||||
(sum, e) => sum + e.transactionCosts,
|
||||
0
|
||||
),
|
||||
});
|
||||
|
||||
return rebalanceEvents;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Risk-Adjusted Returns Analysis
|
||||
// ============================================================================
|
||||
|
||||
interface RiskMetrics {
|
||||
timestamp: Date;
|
||||
portfolioReturn: number;
|
||||
benchmarkReturn: number;
|
||||
excessReturn: number;
|
||||
trackingError: number;
|
||||
informationRatio: number;
|
||||
sharpeRatio: number;
|
||||
sortinoRatio: number;
|
||||
calmarRatio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
correlation: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate comprehensive risk-adjusted return metrics
|
||||
*/
|
||||
async function generateRiskAdjustedReturns() {
|
||||
const { portfolioMetrics } = await generateMultiAssetPortfolio();
|
||||
|
||||
const riskFreeRate = 0.04; // 4% annual
|
||||
const dailyRfRate = riskFreeRate / 252;
|
||||
|
||||
const riskMetrics: RiskMetrics[] = portfolioMetrics.map((metrics, idx) => {
|
||||
// Simulate benchmark (S&P 500)
|
||||
const benchmarkReturn = metrics.dailyReturn * 0.95 + (Math.random() - 0.5) * 0.005;
|
||||
const excessReturn = metrics.dailyReturn - dailyRfRate;
|
||||
|
||||
// Calculate rolling metrics
|
||||
const window = Math.min(60, idx + 1);
|
||||
const recentReturns = portfolioMetrics
|
||||
.slice(Math.max(0, idx - window), idx + 1)
|
||||
.map((m) => m.dailyReturn);
|
||||
|
||||
const recentBenchmarkReturns = Array.from(
|
||||
{ length: window },
|
||||
(_, i) => portfolioMetrics[Math.max(0, idx - window + i)].dailyReturn * 0.95
|
||||
);
|
||||
|
||||
// Tracking error
|
||||
const trackingDiffs = recentReturns.map(
|
||||
(r, i) => r - recentBenchmarkReturns[i]
|
||||
);
|
||||
const trackingError =
|
||||
Math.sqrt(
|
||||
trackingDiffs.reduce((sum, d) => sum + Math.pow(d, 2), 0) / window
|
||||
) * Math.sqrt(252);
|
||||
|
||||
// Information ratio
|
||||
const avgExcessReturn =
|
||||
trackingDiffs.reduce((sum, d) => sum + d, 0) / window;
|
||||
const informationRatio = (avgExcessReturn * 252) / (trackingError + 0.0001);
|
||||
|
||||
// Sortino ratio (downside deviation)
|
||||
const downsideReturns = recentReturns.filter((r) => r < dailyRfRate);
|
||||
const downsideDeviation = downsideReturns.length > 0
|
||||
? Math.sqrt(
|
||||
downsideReturns.reduce(
|
||||
(sum, r) => sum + Math.pow(r - dailyRfRate, 2),
|
||||
0
|
||||
) / downsideReturns.length
|
||||
) * Math.sqrt(252)
|
||||
: 0.0001;
|
||||
|
||||
const avgReturn = recentReturns.reduce((sum, r) => sum + r, 0) / window;
|
||||
const sortinoRatio = ((avgReturn - dailyRfRate) * 252) / downsideDeviation;
|
||||
|
||||
// Calmar ratio
|
||||
const calmarRatio = (avgReturn * 252) / (Math.abs(metrics.maxDrawdown) + 0.0001);
|
||||
|
||||
// Beta and alpha
|
||||
const benchmarkVar =
|
||||
recentBenchmarkReturns.reduce(
|
||||
(sum, r) => sum + Math.pow(r - avgReturn, 2),
|
||||
0
|
||||
) / window;
|
||||
const covariance =
|
||||
recentReturns.reduce(
|
||||
(sum, r, i) => sum + (r - avgReturn) * (recentBenchmarkReturns[i] - avgReturn),
|
||||
0
|
||||
) / window;
|
||||
const beta = covariance / (benchmarkVar + 0.0001);
|
||||
const alpha = (avgReturn - dailyRfRate - beta * (avgReturn * 0.95 - dailyRfRate)) * 252;
|
||||
|
||||
// Correlation
|
||||
const correlation = covariance / (Math.sqrt(benchmarkVar) * metrics.volatility / Math.sqrt(252) + 0.0001);
|
||||
|
||||
return {
|
||||
timestamp: metrics.timestamp,
|
||||
portfolioReturn: metrics.dailyReturn,
|
||||
benchmarkReturn,
|
||||
excessReturn,
|
||||
trackingError: Number(trackingError.toFixed(4)),
|
||||
informationRatio: Number(informationRatio.toFixed(2)),
|
||||
sharpeRatio: metrics.sharpeRatio,
|
||||
sortinoRatio: Number(sortinoRatio.toFixed(2)),
|
||||
calmarRatio: Number(calmarRatio.toFixed(2)),
|
||||
beta: Number(beta.toFixed(2)),
|
||||
alpha: Number(alpha.toFixed(4)),
|
||||
correlation: Number(correlation.toFixed(2)),
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Risk-Adjusted Returns (final metrics):');
|
||||
const finalMetrics = riskMetrics[riskMetrics.length - 1];
|
||||
console.log({
|
||||
sharpeRatio: finalMetrics.sharpeRatio,
|
||||
sortinoRatio: finalMetrics.sortinoRatio,
|
||||
calmarRatio: finalMetrics.calmarRatio,
|
||||
informationRatio: finalMetrics.informationRatio,
|
||||
beta: finalMetrics.beta,
|
||||
alpha: (finalMetrics.alpha * 100).toFixed(2) + '%',
|
||||
correlation: finalMetrics.correlation,
|
||||
});
|
||||
|
||||
return riskMetrics;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Drawdown Analysis
|
||||
// ============================================================================
|
||||
|
||||
interface DrawdownPeriod {
|
||||
startDate: Date;
|
||||
troughDate: Date;
|
||||
endDate: Date | null;
|
||||
peakValue: number;
|
||||
troughValue: number;
|
||||
recoveryValue: number | null;
|
||||
drawdown: number;
|
||||
duration: number;
|
||||
recoveryDuration: number | null;
|
||||
underwater: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze portfolio drawdowns
|
||||
*/
|
||||
async function generateDrawdownAnalysis() {
|
||||
const { portfolioMetrics } = await generateMultiAssetPortfolio();
|
||||
|
||||
const drawdowns: DrawdownPeriod[] = [];
|
||||
let currentPeak = portfolioMetrics[0].totalValue;
|
||||
let currentPeakDate = portfolioMetrics[0].timestamp;
|
||||
let inDrawdown = false;
|
||||
let troughValue = currentPeak;
|
||||
let troughDate = currentPeakDate;
|
||||
let startDate = currentPeakDate;
|
||||
|
||||
portfolioMetrics.forEach((metrics, idx) => {
|
||||
if (metrics.totalValue > currentPeak) {
|
||||
// New peak
|
||||
if (inDrawdown) {
|
||||
// End of drawdown - recovery complete
|
||||
drawdowns[drawdowns.length - 1].endDate = metrics.timestamp;
|
||||
drawdowns[drawdowns.length - 1].recoveryValue = metrics.totalValue;
|
||||
drawdowns[drawdowns.length - 1].recoveryDuration =
|
||||
(metrics.timestamp.getTime() - troughDate.getTime()) / 86400000;
|
||||
drawdowns[drawdowns.length - 1].underwater = false;
|
||||
inDrawdown = false;
|
||||
}
|
||||
currentPeak = metrics.totalValue;
|
||||
currentPeakDate = metrics.timestamp;
|
||||
} else if (metrics.totalValue < currentPeak) {
|
||||
// In drawdown
|
||||
if (!inDrawdown) {
|
||||
// Start of new drawdown
|
||||
startDate = currentPeakDate;
|
||||
troughValue = metrics.totalValue;
|
||||
troughDate = metrics.timestamp;
|
||||
inDrawdown = true;
|
||||
}
|
||||
|
||||
if (metrics.totalValue < troughValue) {
|
||||
troughValue = metrics.totalValue;
|
||||
troughDate = metrics.timestamp;
|
||||
}
|
||||
|
||||
// Update or create drawdown record
|
||||
const dd = (metrics.totalValue - currentPeak) / currentPeak;
|
||||
const duration = (troughDate.getTime() - startDate.getTime()) / 86400000;
|
||||
|
||||
if (drawdowns.length === 0 || !drawdowns[drawdowns.length - 1].underwater) {
|
||||
drawdowns.push({
|
||||
startDate,
|
||||
troughDate,
|
||||
endDate: null,
|
||||
peakValue: currentPeak,
|
||||
troughValue,
|
||||
recoveryValue: null,
|
||||
drawdown: dd,
|
||||
duration,
|
||||
recoveryDuration: null,
|
||||
underwater: true,
|
||||
});
|
||||
} else {
|
||||
drawdowns[drawdowns.length - 1].troughDate = troughDate;
|
||||
drawdowns[drawdowns.length - 1].troughValue = troughValue;
|
||||
drawdowns[drawdowns.length - 1].drawdown = dd;
|
||||
drawdowns[drawdowns.length - 1].duration = duration;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort by drawdown magnitude
|
||||
const sortedDrawdowns = drawdowns.sort((a, b) => a.drawdown - b.drawdown);
|
||||
|
||||
console.log('Drawdown Analysis:');
|
||||
console.log({
|
||||
totalDrawdowns: drawdowns.length,
|
||||
maxDrawdown: (sortedDrawdowns[0].drawdown * 100).toFixed(2) + '%',
|
||||
avgDrawdown: (
|
||||
(drawdowns.reduce((sum, dd) => sum + dd.drawdown, 0) / drawdowns.length) *
|
||||
100
|
||||
).toFixed(2) + '%',
|
||||
longestDrawdown: Math.max(...drawdowns.map((dd) => dd.duration)),
|
||||
currentlyUnderwater: drawdowns[drawdowns.length - 1]?.underwater || false,
|
||||
});
|
||||
|
||||
console.log('\nTop 5 Drawdowns:');
|
||||
sortedDrawdowns.slice(0, 5).forEach((dd, idx) => {
|
||||
console.log(
|
||||
`${idx + 1}. ${(dd.drawdown * 100).toFixed(2)}% over ${dd.duration} days, ` +
|
||||
`recovered in ${dd.recoveryDuration || 'N/A'} days`
|
||||
);
|
||||
});
|
||||
|
||||
return drawdowns;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main Execution
|
||||
// ============================================================================
|
||||
|
||||
async function main() {
|
||||
console.log('='.repeat(80));
|
||||
console.log('Portfolio Simulation and Management');
|
||||
console.log('='.repeat(80));
|
||||
console.log();
|
||||
|
||||
try {
|
||||
console.log('1. Generating Multi-Asset Portfolio...');
|
||||
await generateMultiAssetPortfolio();
|
||||
console.log();
|
||||
|
||||
console.log('2. Generating Rebalancing Scenarios...');
|
||||
await generateRebalancingScenarios();
|
||||
console.log();
|
||||
|
||||
console.log('3. Calculating Risk-Adjusted Returns...');
|
||||
await generateRiskAdjustedReturns();
|
||||
console.log();
|
||||
|
||||
console.log('4. Analyzing Drawdowns...');
|
||||
await generateDrawdownAnalysis();
|
||||
console.log();
|
||||
|
||||
console.log('All portfolio simulations completed successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error generating portfolio simulations:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
export {
|
||||
generateMultiAssetPortfolio,
|
||||
generateRebalancingScenarios,
|
||||
generateRiskAdjustedReturns,
|
||||
generateDrawdownAnalysis,
|
||||
};
|
||||
74
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.d.ts
vendored
Normal file
74
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.d.ts
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Trading Scenarios Generation
|
||||
*
|
||||
* Generate realistic market scenarios for testing trading systems:
|
||||
* - Bull/bear markets
|
||||
* - Volatility patterns
|
||||
* - Flash crashes
|
||||
* - Earnings announcements
|
||||
* - Market correlations
|
||||
*/
|
||||
/**
|
||||
* Generate sustained uptrend with occasional pullbacks
|
||||
*/
|
||||
declare function generateBullMarket(): Promise<any>;
|
||||
/**
|
||||
* Generate sustained downtrend with sharp selloffs
|
||||
*/
|
||||
declare function generateBearMarket(): Promise<any>;
|
||||
interface VolatilityRegime {
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
realizedVol: number;
|
||||
impliedVol: number;
|
||||
vix: number;
|
||||
regime: 'low' | 'medium' | 'high' | 'extreme';
|
||||
symbol: string;
|
||||
}
|
||||
/**
|
||||
* Generate varying volatility regimes
|
||||
*/
|
||||
declare function generateVolatilityPatterns(): Promise<VolatilityRegime[]>;
|
||||
interface FlashCrashEvent {
|
||||
phase: 'normal' | 'crash' | 'recovery';
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
volume: number;
|
||||
spread: number;
|
||||
liquidityScore: number;
|
||||
symbol: string;
|
||||
}
|
||||
/**
|
||||
* Simulate flash crash with rapid price decline and recovery
|
||||
*/
|
||||
declare function generateFlashCrash(): Promise<FlashCrashEvent[]>;
|
||||
interface EarningsEvent {
|
||||
phase: 'pre-announcement' | 'announcement' | 'post-announcement';
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
volume: number;
|
||||
impliedVolatility: number;
|
||||
optionVolume: number;
|
||||
surprise: 'beat' | 'miss' | 'inline';
|
||||
symbol: string;
|
||||
}
|
||||
/**
|
||||
* Simulate earnings announcement with volatility crush
|
||||
*/
|
||||
declare function generateEarningsScenario(): Promise<EarningsEvent[]>;
|
||||
interface CorrelationData {
|
||||
timestamp: Date;
|
||||
spy: number;
|
||||
qqq: number;
|
||||
iwm: number;
|
||||
vix: number;
|
||||
dxy: number;
|
||||
correlation_spy_qqq: number;
|
||||
correlation_spy_vix: number;
|
||||
}
|
||||
/**
|
||||
* Generate correlated multi-asset data
|
||||
*/
|
||||
declare function generateCorrelatedMarkets(): Promise<CorrelationData[]>;
|
||||
export { generateBullMarket, generateBearMarket, generateVolatilityPatterns, generateFlashCrash, generateEarningsScenario, generateCorrelatedMarkets, };
|
||||
//# sourceMappingURL=trading-scenarios.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"trading-scenarios.d.ts","sourceRoot":"","sources":["trading-scenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAyBH;;GAEG;AACH,iBAAe,kBAAkB,iBA2DhC;AAMD;;GAEG;AACH,iBAAe,kBAAkB,iBA4DhC;AAMD,UAAU,gBAAgB;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,iBAAe,0BAA0B,gCAyCxC;AAMD,UAAU,eAAe;IACvB,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;IACvC,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,iBAAe,kBAAkB,+BAiFhC;AAMD,UAAU,aAAa;IACrB,KAAK,EAAE,kBAAkB,GAAG,cAAc,GAAG,mBAAmB,CAAC;IACjE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,iBAAe,wBAAwB,6BA+FtC;AAMD,UAAU,eAAe;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,iBAAe,yBAAyB,+BAmFvC;AAgDD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,wBAAwB,EACxB,yBAAyB,GAC1B,CAAC"}
|
||||
427
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.js
vendored
Normal file
427
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.js
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Trading Scenarios Generation
|
||||
*
|
||||
* Generate realistic market scenarios for testing trading systems:
|
||||
* - Bull/bear markets
|
||||
* - Volatility patterns
|
||||
* - Flash crashes
|
||||
* - Earnings announcements
|
||||
* - Market correlations
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.generateBullMarket = generateBullMarket;
|
||||
exports.generateBearMarket = generateBearMarket;
|
||||
exports.generateVolatilityPatterns = generateVolatilityPatterns;
|
||||
exports.generateFlashCrash = generateFlashCrash;
|
||||
exports.generateEarningsScenario = generateEarningsScenario;
|
||||
exports.generateCorrelatedMarkets = generateCorrelatedMarkets;
|
||||
const src_1 = require("../../../src");
|
||||
// ============================================================================
|
||||
// Bull Market Scenario
|
||||
// ============================================================================
|
||||
/**
|
||||
* Generate sustained uptrend with occasional pullbacks
|
||||
*/
|
||||
async function generateBullMarket() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const basePrice = 100;
|
||||
const days = 252; // One trading year
|
||||
const barsPerDay = 390; // 1-minute bars
|
||||
const bullMarket = await synth.generate({
|
||||
count: days * barsPerDay,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(1000000, 5000000)}}',
|
||||
regime: 'bull',
|
||||
volatility: '{{number.float(0.01, 0.03, 4)}}',
|
||||
trend: '{{number.float(0.5, 1, 2)}}',
|
||||
momentum: '{{number.float(0.3, 0.9, 2)}}',
|
||||
symbol: 'BULL',
|
||||
},
|
||||
});
|
||||
// Generate price series with upward drift
|
||||
let currentPrice = basePrice;
|
||||
const enrichedData = bullMarket.map((bar, idx) => {
|
||||
// Daily drift: ~0.08% per bar (20% annual return)
|
||||
const drift = 0.0008;
|
||||
const volatility = bar.volatility;
|
||||
const random = (Math.random() - 0.5) * 2; // -1 to 1
|
||||
// Occasional pullbacks (10% chance)
|
||||
const pullback = Math.random() < 0.1 ? -0.002 : 0;
|
||||
currentPrice *= 1 + drift + volatility * random + pullback;
|
||||
// Increase volume on breakouts
|
||||
const volumeMultiplier = currentPrice > basePrice * 1.1 ? 1.5 : 1;
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: Math.floor(bar.volume * volumeMultiplier),
|
||||
};
|
||||
});
|
||||
console.log('Bull Market Scenario:');
|
||||
console.log({
|
||||
initialPrice: enrichedData[0].price,
|
||||
finalPrice: enrichedData[enrichedData.length - 1].price,
|
||||
totalReturn: (((enrichedData[enrichedData.length - 1].price - enrichedData[0].price) /
|
||||
enrichedData[0].price) *
|
||||
100).toFixed(2) + '%',
|
||||
avgVolatility: enrichedData.reduce((sum, b) => sum + b.volatility, 0) /
|
||||
enrichedData.length,
|
||||
});
|
||||
return enrichedData;
|
||||
}
|
||||
// ============================================================================
|
||||
// Bear Market Scenario
|
||||
// ============================================================================
|
||||
/**
|
||||
* Generate sustained downtrend with sharp selloffs
|
||||
*/
|
||||
async function generateBearMarket() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const basePrice = 100;
|
||||
const days = 126; // Six months of bear market
|
||||
const bearMarket = await synth.generate({
|
||||
count: days * 390,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(2000000, 8000000)}}', // Higher volume in bear
|
||||
regime: 'bear',
|
||||
volatility: '{{number.float(0.02, 0.06, 4)}}', // Higher volatility
|
||||
trend: '{{number.float(-1, -0.4, 2)}}',
|
||||
momentum: '{{number.float(-0.9, -0.3, 2)}}',
|
||||
symbol: 'BEAR',
|
||||
},
|
||||
});
|
||||
let currentPrice = basePrice;
|
||||
const enrichedData = bearMarket.map((bar, idx) => {
|
||||
// Daily drift: -0.1% per bar (-25% over 6 months)
|
||||
const drift = -0.001;
|
||||
const volatility = bar.volatility;
|
||||
const random = (Math.random() - 0.5) * 2;
|
||||
// Sharp selloffs (5% chance of -2% move)
|
||||
const selloff = Math.random() < 0.05 ? -0.02 : 0;
|
||||
// Dead cat bounces (3% chance of +1.5% move)
|
||||
const bounce = Math.random() < 0.03 ? 0.015 : 0;
|
||||
currentPrice *= 1 + drift + volatility * random + selloff + bounce;
|
||||
// Volume spikes on panic selling
|
||||
const volumeMultiplier = selloff < 0 ? 2.5 : 1;
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: Math.floor(bar.volume * volumeMultiplier),
|
||||
};
|
||||
});
|
||||
console.log('Bear Market Scenario:');
|
||||
console.log({
|
||||
initialPrice: enrichedData[0].price,
|
||||
finalPrice: enrichedData[enrichedData.length - 1].price,
|
||||
totalReturn: (((enrichedData[enrichedData.length - 1].price - enrichedData[0].price) /
|
||||
enrichedData[0].price) *
|
||||
100).toFixed(2) + '%',
|
||||
avgVolatility: enrichedData.reduce((sum, b) => sum + b.volatility, 0) /
|
||||
enrichedData.length,
|
||||
});
|
||||
return enrichedData;
|
||||
}
|
||||
/**
|
||||
* Generate varying volatility regimes
|
||||
*/
|
||||
async function generateVolatilityPatterns() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const scenarios = [
|
||||
{ regime: 'low', vixRange: [10, 15], volRange: [0.005, 0.015] },
|
||||
{ regime: 'medium', vixRange: [15, 25], volRange: [0.015, 0.03] },
|
||||
{ regime: 'high', vixRange: [25, 40], volRange: [0.03, 0.05] },
|
||||
{ regime: 'extreme', vixRange: [40, 80], volRange: [0.05, 0.15] },
|
||||
];
|
||||
const allScenarios = [];
|
||||
for (const scenario of scenarios) {
|
||||
const data = await synth.generate({
|
||||
count: 390, // One trading day
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
price: '{{number.float(100, 200, 2)}}',
|
||||
realizedVol: `{{number.float(${scenario.volRange[0]}, ${scenario.volRange[1]}, 4)}}`,
|
||||
impliedVol: `{{number.float(${scenario.volRange[0] * 1.1}, ${scenario.volRange[1] * 1.2}, 4)}}`,
|
||||
vix: `{{number.float(${scenario.vixRange[0]}, ${scenario.vixRange[1]}, 2)}}`,
|
||||
regime: scenario.regime,
|
||||
symbol: 'SPY',
|
||||
},
|
||||
constraints: [
|
||||
'bar.impliedVol >= bar.realizedVol', // IV typically > RV
|
||||
],
|
||||
});
|
||||
allScenarios.push(...data);
|
||||
}
|
||||
console.log('Volatility Patterns Generated:');
|
||||
scenarios.forEach((s) => {
|
||||
const filtered = allScenarios.filter((d) => d.regime === s.regime);
|
||||
console.log(`${s.regime}: ${filtered.length} bars, avg VIX: ${(filtered.reduce((sum, b) => sum + b.vix, 0) / filtered.length).toFixed(2)}`);
|
||||
});
|
||||
return allScenarios;
|
||||
}
|
||||
/**
|
||||
* Simulate flash crash with rapid price decline and recovery
|
||||
*/
|
||||
async function generateFlashCrash() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const basePrice = 150;
|
||||
const phases = [
|
||||
{ phase: 'normal', duration: 100, priceChange: 0 },
|
||||
{ phase: 'crash', duration: 20, priceChange: -0.15 }, // 15% drop
|
||||
{ phase: 'recovery', duration: 50, priceChange: 0.12 }, // Recover 12%
|
||||
];
|
||||
const allData = [];
|
||||
let currentPrice = basePrice;
|
||||
for (const phase of phases) {
|
||||
const phaseData = await synth.generate({
|
||||
count: phase.duration,
|
||||
template: {
|
||||
phase: phase.phase,
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(1000000, 10000000)}}',
|
||||
spread: '{{number.float(0.01, 0.5, 4)}}',
|
||||
liquidityScore: '{{number.float(0, 1, 2)}}',
|
||||
symbol: 'FLASH',
|
||||
},
|
||||
});
|
||||
const pricePerBar = phase.priceChange / phase.duration;
|
||||
const enrichedPhase = phaseData.map((bar, idx) => {
|
||||
if (phase.phase === 'crash') {
|
||||
// Exponential decay during crash
|
||||
const crashIntensity = Math.pow(idx / phase.duration, 2);
|
||||
currentPrice *= 1 + pricePerBar * (1 + crashIntensity);
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: bar.volume * 5, // Massive volume spike
|
||||
spread: bar.spread * 10, // Wide spreads
|
||||
liquidityScore: 0.1, // Liquidity evaporates
|
||||
};
|
||||
}
|
||||
else if (phase.phase === 'recovery') {
|
||||
// Quick recovery
|
||||
currentPrice *= 1 + pricePerBar * 1.5;
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: bar.volume * 2,
|
||||
spread: bar.spread * 3,
|
||||
liquidityScore: 0.4,
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Normal trading
|
||||
currentPrice *= 1 + (Math.random() - 0.5) * 0.0002;
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
liquidityScore: 0.9,
|
||||
};
|
||||
}
|
||||
});
|
||||
allData.push(...enrichedPhase);
|
||||
}
|
||||
console.log('Flash Crash Simulation:');
|
||||
console.log({
|
||||
precrashPrice: allData[99].price,
|
||||
crashLowPrice: Math.min(...allData.slice(100, 120).map((d) => d.price)),
|
||||
postRecoveryPrice: allData[allData.length - 1].price,
|
||||
maxDrawdown: (((Math.min(...allData.map((d) => d.price)) - allData[0].price) /
|
||||
allData[0].price) *
|
||||
100).toFixed(2) + '%',
|
||||
});
|
||||
return allData;
|
||||
}
|
||||
/**
|
||||
* Simulate earnings announcement with volatility crush
|
||||
*/
|
||||
async function generateEarningsScenario() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const surpriseType = ['beat', 'miss', 'inline'][Math.floor(Math.random() * 3)];
|
||||
const phases = [
|
||||
{ phase: 'pre-announcement', duration: 200, ivLevel: 0.8 },
|
||||
{ phase: 'announcement', duration: 10, ivLevel: 1.2 },
|
||||
{ phase: 'post-announcement', duration: 180, ivLevel: 0.3 },
|
||||
];
|
||||
const allData = [];
|
||||
let basePrice = 100;
|
||||
// Determine price reaction based on surprise
|
||||
const priceReaction = {
|
||||
beat: 0.08, // 8% pop
|
||||
miss: -0.12, // 12% drop
|
||||
inline: 0.02, // 2% drift
|
||||
}[surpriseType];
|
||||
for (const phase of phases) {
|
||||
const phaseData = await synth.generate({
|
||||
count: phase.duration,
|
||||
template: {
|
||||
phase: phase.phase,
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(1000000, 5000000)}}',
|
||||
impliedVolatility: 0,
|
||||
optionVolume: '{{number.int(10000, 100000)}}',
|
||||
surprise: surpriseType,
|
||||
symbol: 'EARN',
|
||||
},
|
||||
});
|
||||
const enrichedPhase = phaseData.map((bar, idx) => {
|
||||
if (phase.phase === 'pre-announcement') {
|
||||
// Building anticipation
|
||||
basePrice *= 1 + (Math.random() - 0.5) * 0.001;
|
||||
return {
|
||||
...bar,
|
||||
price: Number(basePrice.toFixed(2)),
|
||||
impliedVolatility: Number((phase.ivLevel * (0.3 + idx / phase.duration * 0.2)).toFixed(4)),
|
||||
optionVolume: bar.optionVolume * 2, // Heavy options activity
|
||||
};
|
||||
}
|
||||
else if (phase.phase === 'announcement') {
|
||||
// Immediate reaction
|
||||
if (idx === 0) {
|
||||
basePrice *= 1 + priceReaction;
|
||||
}
|
||||
return {
|
||||
...bar,
|
||||
price: Number(basePrice.toFixed(2)),
|
||||
volume: bar.volume * 10, // Massive volume spike
|
||||
impliedVolatility: Number((phase.ivLevel * 0.5).toFixed(4)),
|
||||
optionVolume: bar.optionVolume * 5,
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Volatility crush
|
||||
basePrice *= 1 + (Math.random() - 0.5) * 0.0005;
|
||||
return {
|
||||
...bar,
|
||||
price: Number(basePrice.toFixed(2)),
|
||||
impliedVolatility: Number((phase.ivLevel * (1 - idx / phase.duration * 0.7)).toFixed(4)),
|
||||
volume: Math.floor(bar.volume * (2 - idx / phase.duration)),
|
||||
};
|
||||
}
|
||||
});
|
||||
allData.push(...enrichedPhase);
|
||||
}
|
||||
console.log('Earnings Announcement Scenario:');
|
||||
console.log({
|
||||
surprise: surpriseType,
|
||||
preEarningsPrice: allData[199].price,
|
||||
postEarningsPrice: allData[210].price,
|
||||
priceChange: (((allData[210].price - allData[199].price) / allData[199].price) *
|
||||
100).toFixed(2) + '%',
|
||||
preIV: allData[199].impliedVolatility,
|
||||
postIV: allData[allData.length - 1].impliedVolatility,
|
||||
ivCrush: (((allData[allData.length - 1].impliedVolatility - allData[199].impliedVolatility) /
|
||||
allData[199].impliedVolatility) *
|
||||
100).toFixed(2) + '%',
|
||||
});
|
||||
return allData;
|
||||
}
|
||||
/**
|
||||
* Generate correlated multi-asset data
|
||||
*/
|
||||
async function generateCorrelatedMarkets() {
|
||||
const synth = new src_1.AgenticSynth();
|
||||
const count = 390;
|
||||
const baseData = await synth.generate({
|
||||
count,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
},
|
||||
});
|
||||
// Generate correlated returns
|
||||
const returns = Array.from({ length: count }, () => {
|
||||
const marketFactor = (Math.random() - 0.5) * 0.02; // Common market movement
|
||||
return {
|
||||
spy: marketFactor + (Math.random() - 0.5) * 0.005,
|
||||
qqq: marketFactor * 1.3 + (Math.random() - 0.5) * 0.008, // Higher beta
|
||||
iwm: marketFactor * 1.5 + (Math.random() - 0.5) * 0.01, // Even higher beta
|
||||
vix: -marketFactor * 3 + (Math.random() - 0.5) * 0.05, // Negative correlation
|
||||
dxy: -marketFactor * 0.5 + (Math.random() - 0.5) * 0.003, // Slight negative
|
||||
};
|
||||
});
|
||||
// Convert returns to prices
|
||||
let prices = { spy: 400, qqq: 350, iwm: 180, vix: 15, dxy: 100 };
|
||||
const correlationData = baseData.map((bar, idx) => {
|
||||
prices.spy *= 1 + returns[idx].spy;
|
||||
prices.qqq *= 1 + returns[idx].qqq;
|
||||
prices.iwm *= 1 + returns[idx].iwm;
|
||||
prices.vix *= 1 + returns[idx].vix;
|
||||
prices.dxy *= 1 + returns[idx].dxy;
|
||||
// Calculate rolling correlation (simplified)
|
||||
const window = 20;
|
||||
const start = Math.max(0, idx - window);
|
||||
const spyReturns = returns.slice(start, idx + 1).map((r) => r.spy);
|
||||
const qqqReturns = returns.slice(start, idx + 1).map((r) => r.qqq);
|
||||
const vixReturns = returns.slice(start, idx + 1).map((r) => r.vix);
|
||||
const correlation = (arr1, arr2) => {
|
||||
const n = arr1.length;
|
||||
const mean1 = arr1.reduce((a, b) => a + b, 0) / n;
|
||||
const mean2 = arr2.reduce((a, b) => a + b, 0) / n;
|
||||
const numerator = arr1.reduce((sum, val, i) => sum + (val - mean1) * (arr2[i] - mean2), 0);
|
||||
const denom1 = Math.sqrt(arr1.reduce((sum, val) => sum + Math.pow(val - mean1, 2), 0));
|
||||
const denom2 = Math.sqrt(arr2.reduce((sum, val) => sum + Math.pow(val - mean2, 2), 0));
|
||||
return numerator / (denom1 * denom2);
|
||||
};
|
||||
return {
|
||||
timestamp: bar.timestamp,
|
||||
spy: Number(prices.spy.toFixed(2)),
|
||||
qqq: Number(prices.qqq.toFixed(2)),
|
||||
iwm: Number(prices.iwm.toFixed(2)),
|
||||
vix: Number(prices.vix.toFixed(2)),
|
||||
dxy: Number(prices.dxy.toFixed(2)),
|
||||
correlation_spy_qqq: Number(correlation(spyReturns, qqqReturns).toFixed(4)),
|
||||
correlation_spy_vix: Number(correlation(spyReturns, vixReturns).toFixed(4)),
|
||||
};
|
||||
});
|
||||
console.log('Market Correlation Data:');
|
||||
console.log({
|
||||
avgCorrelation_SPY_QQQ: correlationData.reduce((sum, d) => sum + d.correlation_spy_qqq, 0) /
|
||||
correlationData.length,
|
||||
avgCorrelation_SPY_VIX: correlationData.reduce((sum, d) => sum + d.correlation_spy_vix, 0) /
|
||||
correlationData.length,
|
||||
});
|
||||
return correlationData;
|
||||
}
|
||||
// ============================================================================
|
||||
// Main Execution
|
||||
// ============================================================================
|
||||
async function main() {
|
||||
console.log('='.repeat(80));
|
||||
console.log('Trading Scenarios Generation');
|
||||
console.log('='.repeat(80));
|
||||
console.log();
|
||||
try {
|
||||
console.log('1. Generating Bull Market Scenario...');
|
||||
await generateBullMarket();
|
||||
console.log();
|
||||
console.log('2. Generating Bear Market Scenario...');
|
||||
await generateBearMarket();
|
||||
console.log();
|
||||
console.log('3. Generating Volatility Patterns...');
|
||||
await generateVolatilityPatterns();
|
||||
console.log();
|
||||
console.log('4. Generating Flash Crash...');
|
||||
await generateFlashCrash();
|
||||
console.log();
|
||||
console.log('5. Generating Earnings Scenario...');
|
||||
await generateEarningsScenario();
|
||||
console.log();
|
||||
console.log('6. Generating Correlated Markets...');
|
||||
await generateCorrelatedMarkets();
|
||||
console.log();
|
||||
console.log('All trading scenarios generated successfully!');
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error generating trading scenarios:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
//# sourceMappingURL=trading-scenarios.js.map
|
||||
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
599
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.ts
vendored
Normal file
599
vendor/ruvector/npm/packages/agentic-synth/examples/stocks/trading-scenarios.ts
vendored
Normal file
@@ -0,0 +1,599 @@
|
||||
/**
|
||||
* Trading Scenarios Generation
|
||||
*
|
||||
* Generate realistic market scenarios for testing trading systems:
|
||||
* - Bull/bear markets
|
||||
* - Volatility patterns
|
||||
* - Flash crashes
|
||||
* - Earnings announcements
|
||||
* - Market correlations
|
||||
*/
|
||||
|
||||
import { AgenticSynth } from '../../../src';
|
||||
|
||||
// ============================================================================
|
||||
// Market Regime Types
|
||||
// ============================================================================
|
||||
|
||||
type MarketRegime = 'bull' | 'bear' | 'sideways' | 'volatile' | 'crisis';
|
||||
|
||||
interface MarketScenario {
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
volume: number;
|
||||
regime: MarketRegime;
|
||||
volatility: number;
|
||||
trend: number; // -1 to 1
|
||||
momentum: number;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Bull Market Scenario
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Generate sustained uptrend with occasional pullbacks
|
||||
*/
|
||||
async function generateBullMarket() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const basePrice = 100;
|
||||
const days = 252; // One trading year
|
||||
const barsPerDay = 390; // 1-minute bars
|
||||
|
||||
const bullMarket = await synth.generate<MarketScenario>({
|
||||
count: days * barsPerDay,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(1000000, 5000000)}}',
|
||||
regime: 'bull',
|
||||
volatility: '{{number.float(0.01, 0.03, 4)}}',
|
||||
trend: '{{number.float(0.5, 1, 2)}}',
|
||||
momentum: '{{number.float(0.3, 0.9, 2)}}',
|
||||
symbol: 'BULL',
|
||||
},
|
||||
});
|
||||
|
||||
// Generate price series with upward drift
|
||||
let currentPrice = basePrice;
|
||||
const enrichedData = bullMarket.map((bar, idx) => {
|
||||
// Daily drift: ~0.08% per bar (20% annual return)
|
||||
const drift = 0.0008;
|
||||
const volatility = bar.volatility;
|
||||
const random = (Math.random() - 0.5) * 2; // -1 to 1
|
||||
|
||||
// Occasional pullbacks (10% chance)
|
||||
const pullback = Math.random() < 0.1 ? -0.002 : 0;
|
||||
|
||||
currentPrice *= 1 + drift + volatility * random + pullback;
|
||||
|
||||
// Increase volume on breakouts
|
||||
const volumeMultiplier = currentPrice > basePrice * 1.1 ? 1.5 : 1;
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: Math.floor(bar.volume * volumeMultiplier),
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Bull Market Scenario:');
|
||||
console.log({
|
||||
initialPrice: enrichedData[0].price,
|
||||
finalPrice: enrichedData[enrichedData.length - 1].price,
|
||||
totalReturn: (
|
||||
((enrichedData[enrichedData.length - 1].price - enrichedData[0].price) /
|
||||
enrichedData[0].price) *
|
||||
100
|
||||
).toFixed(2) + '%',
|
||||
avgVolatility:
|
||||
enrichedData.reduce((sum, b) => sum + b.volatility, 0) /
|
||||
enrichedData.length,
|
||||
});
|
||||
|
||||
return enrichedData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Bear Market Scenario
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Generate sustained downtrend with sharp selloffs
|
||||
*/
|
||||
async function generateBearMarket() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const basePrice = 100;
|
||||
const days = 126; // Six months of bear market
|
||||
|
||||
const bearMarket = await synth.generate<MarketScenario>({
|
||||
count: days * 390,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(2000000, 8000000)}}', // Higher volume in bear
|
||||
regime: 'bear',
|
||||
volatility: '{{number.float(0.02, 0.06, 4)}}', // Higher volatility
|
||||
trend: '{{number.float(-1, -0.4, 2)}}',
|
||||
momentum: '{{number.float(-0.9, -0.3, 2)}}',
|
||||
symbol: 'BEAR',
|
||||
},
|
||||
});
|
||||
|
||||
let currentPrice = basePrice;
|
||||
const enrichedData = bearMarket.map((bar, idx) => {
|
||||
// Daily drift: -0.1% per bar (-25% over 6 months)
|
||||
const drift = -0.001;
|
||||
const volatility = bar.volatility;
|
||||
const random = (Math.random() - 0.5) * 2;
|
||||
|
||||
// Sharp selloffs (5% chance of -2% move)
|
||||
const selloff = Math.random() < 0.05 ? -0.02 : 0;
|
||||
|
||||
// Dead cat bounces (3% chance of +1.5% move)
|
||||
const bounce = Math.random() < 0.03 ? 0.015 : 0;
|
||||
|
||||
currentPrice *= 1 + drift + volatility * random + selloff + bounce;
|
||||
|
||||
// Volume spikes on panic selling
|
||||
const volumeMultiplier = selloff < 0 ? 2.5 : 1;
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: Math.floor(bar.volume * volumeMultiplier),
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Bear Market Scenario:');
|
||||
console.log({
|
||||
initialPrice: enrichedData[0].price,
|
||||
finalPrice: enrichedData[enrichedData.length - 1].price,
|
||||
totalReturn: (
|
||||
((enrichedData[enrichedData.length - 1].price - enrichedData[0].price) /
|
||||
enrichedData[0].price) *
|
||||
100
|
||||
).toFixed(2) + '%',
|
||||
avgVolatility:
|
||||
enrichedData.reduce((sum, b) => sum + b.volatility, 0) /
|
||||
enrichedData.length,
|
||||
});
|
||||
|
||||
return enrichedData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Volatility Patterns
|
||||
// ============================================================================
|
||||
|
||||
interface VolatilityRegime {
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
realizedVol: number;
|
||||
impliedVol: number;
|
||||
vix: number;
|
||||
regime: 'low' | 'medium' | 'high' | 'extreme';
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate varying volatility regimes
|
||||
*/
|
||||
async function generateVolatilityPatterns() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const scenarios = [
|
||||
{ regime: 'low', vixRange: [10, 15], volRange: [0.005, 0.015] },
|
||||
{ regime: 'medium', vixRange: [15, 25], volRange: [0.015, 0.03] },
|
||||
{ regime: 'high', vixRange: [25, 40], volRange: [0.03, 0.05] },
|
||||
{ regime: 'extreme', vixRange: [40, 80], volRange: [0.05, 0.15] },
|
||||
] as const;
|
||||
|
||||
const allScenarios: VolatilityRegime[] = [];
|
||||
|
||||
for (const scenario of scenarios) {
|
||||
const data = await synth.generate<VolatilityRegime>({
|
||||
count: 390, // One trading day
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
price: '{{number.float(100, 200, 2)}}',
|
||||
realizedVol: `{{number.float(${scenario.volRange[0]}, ${scenario.volRange[1]}, 4)}}`,
|
||||
impliedVol: `{{number.float(${scenario.volRange[0] * 1.1}, ${scenario.volRange[1] * 1.2}, 4)}}`,
|
||||
vix: `{{number.float(${scenario.vixRange[0]}, ${scenario.vixRange[1]}, 2)}}`,
|
||||
regime: scenario.regime,
|
||||
symbol: 'SPY',
|
||||
},
|
||||
constraints: [
|
||||
'bar.impliedVol >= bar.realizedVol', // IV typically > RV
|
||||
],
|
||||
});
|
||||
|
||||
allScenarios.push(...data);
|
||||
}
|
||||
|
||||
console.log('Volatility Patterns Generated:');
|
||||
scenarios.forEach((s) => {
|
||||
const filtered = allScenarios.filter((d) => d.regime === s.regime);
|
||||
console.log(`${s.regime}: ${filtered.length} bars, avg VIX: ${
|
||||
(filtered.reduce((sum, b) => sum + b.vix, 0) / filtered.length).toFixed(2)
|
||||
}`);
|
||||
});
|
||||
|
||||
return allScenarios;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Flash Crash Simulation
|
||||
// ============================================================================
|
||||
|
||||
interface FlashCrashEvent {
|
||||
phase: 'normal' | 'crash' | 'recovery';
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
volume: number;
|
||||
spread: number;
|
||||
liquidityScore: number;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate flash crash with rapid price decline and recovery
|
||||
*/
|
||||
async function generateFlashCrash() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const basePrice = 150;
|
||||
const phases = [
|
||||
{ phase: 'normal', duration: 100, priceChange: 0 },
|
||||
{ phase: 'crash', duration: 20, priceChange: -0.15 }, // 15% drop
|
||||
{ phase: 'recovery', duration: 50, priceChange: 0.12 }, // Recover 12%
|
||||
] as const;
|
||||
|
||||
const allData: FlashCrashEvent[] = [];
|
||||
let currentPrice = basePrice;
|
||||
|
||||
for (const phase of phases) {
|
||||
const phaseData = await synth.generate<FlashCrashEvent>({
|
||||
count: phase.duration,
|
||||
template: {
|
||||
phase: phase.phase,
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(1000000, 10000000)}}',
|
||||
spread: '{{number.float(0.01, 0.5, 4)}}',
|
||||
liquidityScore: '{{number.float(0, 1, 2)}}',
|
||||
symbol: 'FLASH',
|
||||
},
|
||||
});
|
||||
|
||||
const pricePerBar = phase.priceChange / phase.duration;
|
||||
|
||||
const enrichedPhase = phaseData.map((bar, idx) => {
|
||||
if (phase.phase === 'crash') {
|
||||
// Exponential decay during crash
|
||||
const crashIntensity = Math.pow(idx / phase.duration, 2);
|
||||
currentPrice *= 1 + pricePerBar * (1 + crashIntensity);
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: bar.volume * 5, // Massive volume spike
|
||||
spread: bar.spread * 10, // Wide spreads
|
||||
liquidityScore: 0.1, // Liquidity evaporates
|
||||
};
|
||||
} else if (phase.phase === 'recovery') {
|
||||
// Quick recovery
|
||||
currentPrice *= 1 + pricePerBar * 1.5;
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
volume: bar.volume * 2,
|
||||
spread: bar.spread * 3,
|
||||
liquidityScore: 0.4,
|
||||
};
|
||||
} else {
|
||||
// Normal trading
|
||||
currentPrice *= 1 + (Math.random() - 0.5) * 0.0002;
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(currentPrice.toFixed(2)),
|
||||
liquidityScore: 0.9,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
allData.push(...enrichedPhase);
|
||||
}
|
||||
|
||||
console.log('Flash Crash Simulation:');
|
||||
console.log({
|
||||
precrashPrice: allData[99].price,
|
||||
crashLowPrice: Math.min(...allData.slice(100, 120).map((d) => d.price)),
|
||||
postRecoveryPrice: allData[allData.length - 1].price,
|
||||
maxDrawdown: (
|
||||
((Math.min(...allData.map((d) => d.price)) - allData[0].price) /
|
||||
allData[0].price) *
|
||||
100
|
||||
).toFixed(2) + '%',
|
||||
});
|
||||
|
||||
return allData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Earnings Announcement Impact
|
||||
// ============================================================================
|
||||
|
||||
interface EarningsEvent {
|
||||
phase: 'pre-announcement' | 'announcement' | 'post-announcement';
|
||||
timestamp: Date;
|
||||
price: number;
|
||||
volume: number;
|
||||
impliedVolatility: number;
|
||||
optionVolume: number;
|
||||
surprise: 'beat' | 'miss' | 'inline';
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate earnings announcement with volatility crush
|
||||
*/
|
||||
async function generateEarningsScenario() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const surpriseType = ['beat', 'miss', 'inline'][Math.floor(Math.random() * 3)] as 'beat' | 'miss' | 'inline';
|
||||
|
||||
const phases = [
|
||||
{ phase: 'pre-announcement', duration: 200, ivLevel: 0.8 },
|
||||
{ phase: 'announcement', duration: 10, ivLevel: 1.2 },
|
||||
{ phase: 'post-announcement', duration: 180, ivLevel: 0.3 },
|
||||
] as const;
|
||||
|
||||
const allData: EarningsEvent[] = [];
|
||||
let basePrice = 100;
|
||||
|
||||
// Determine price reaction based on surprise
|
||||
const priceReaction = {
|
||||
beat: 0.08, // 8% pop
|
||||
miss: -0.12, // 12% drop
|
||||
inline: 0.02, // 2% drift
|
||||
}[surpriseType];
|
||||
|
||||
for (const phase of phases) {
|
||||
const phaseData = await synth.generate<EarningsEvent>({
|
||||
count: phase.duration,
|
||||
template: {
|
||||
phase: phase.phase,
|
||||
timestamp: '{{date.recent}}',
|
||||
price: 0,
|
||||
volume: '{{number.int(1000000, 5000000)}}',
|
||||
impliedVolatility: 0,
|
||||
optionVolume: '{{number.int(10000, 100000)}}',
|
||||
surprise: surpriseType,
|
||||
symbol: 'EARN',
|
||||
},
|
||||
});
|
||||
|
||||
const enrichedPhase = phaseData.map((bar, idx) => {
|
||||
if (phase.phase === 'pre-announcement') {
|
||||
// Building anticipation
|
||||
basePrice *= 1 + (Math.random() - 0.5) * 0.001;
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(basePrice.toFixed(2)),
|
||||
impliedVolatility: Number((phase.ivLevel * (0.3 + idx / phase.duration * 0.2)).toFixed(4)),
|
||||
optionVolume: bar.optionVolume * 2, // Heavy options activity
|
||||
};
|
||||
} else if (phase.phase === 'announcement') {
|
||||
// Immediate reaction
|
||||
if (idx === 0) {
|
||||
basePrice *= 1 + priceReaction;
|
||||
}
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(basePrice.toFixed(2)),
|
||||
volume: bar.volume * 10, // Massive volume spike
|
||||
impliedVolatility: Number((phase.ivLevel * 0.5).toFixed(4)),
|
||||
optionVolume: bar.optionVolume * 5,
|
||||
};
|
||||
} else {
|
||||
// Volatility crush
|
||||
basePrice *= 1 + (Math.random() - 0.5) * 0.0005;
|
||||
|
||||
return {
|
||||
...bar,
|
||||
price: Number(basePrice.toFixed(2)),
|
||||
impliedVolatility: Number((phase.ivLevel * (1 - idx / phase.duration * 0.7)).toFixed(4)),
|
||||
volume: Math.floor(bar.volume * (2 - idx / phase.duration)),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
allData.push(...enrichedPhase);
|
||||
}
|
||||
|
||||
console.log('Earnings Announcement Scenario:');
|
||||
console.log({
|
||||
surprise: surpriseType,
|
||||
preEarningsPrice: allData[199].price,
|
||||
postEarningsPrice: allData[210].price,
|
||||
priceChange: (
|
||||
((allData[210].price - allData[199].price) / allData[199].price) *
|
||||
100
|
||||
).toFixed(2) + '%',
|
||||
preIV: allData[199].impliedVolatility,
|
||||
postIV: allData[allData.length - 1].impliedVolatility,
|
||||
ivCrush: (
|
||||
((allData[allData.length - 1].impliedVolatility - allData[199].impliedVolatility) /
|
||||
allData[199].impliedVolatility) *
|
||||
100
|
||||
).toFixed(2) + '%',
|
||||
});
|
||||
|
||||
return allData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Market Correlation Data
|
||||
// ============================================================================
|
||||
|
||||
interface CorrelationData {
|
||||
timestamp: Date;
|
||||
spy: number; // S&P 500
|
||||
qqq: number; // Nasdaq
|
||||
iwm: number; // Russell 2000
|
||||
vix: number; // Volatility index
|
||||
dxy: number; // Dollar index
|
||||
correlation_spy_qqq: number;
|
||||
correlation_spy_vix: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate correlated multi-asset data
|
||||
*/
|
||||
async function generateCorrelatedMarkets() {
|
||||
const synth = new AgenticSynth();
|
||||
|
||||
const count = 390;
|
||||
const baseData = await synth.generate<{ timestamp: Date }>({
|
||||
count,
|
||||
template: {
|
||||
timestamp: '{{date.recent}}',
|
||||
},
|
||||
});
|
||||
|
||||
// Generate correlated returns
|
||||
const returns = Array.from({ length: count }, () => {
|
||||
const marketFactor = (Math.random() - 0.5) * 0.02; // Common market movement
|
||||
|
||||
return {
|
||||
spy: marketFactor + (Math.random() - 0.5) * 0.005,
|
||||
qqq: marketFactor * 1.3 + (Math.random() - 0.5) * 0.008, // Higher beta
|
||||
iwm: marketFactor * 1.5 + (Math.random() - 0.5) * 0.01, // Even higher beta
|
||||
vix: -marketFactor * 3 + (Math.random() - 0.5) * 0.05, // Negative correlation
|
||||
dxy: -marketFactor * 0.5 + (Math.random() - 0.5) * 0.003, // Slight negative
|
||||
};
|
||||
});
|
||||
|
||||
// Convert returns to prices
|
||||
let prices = { spy: 400, qqq: 350, iwm: 180, vix: 15, dxy: 100 };
|
||||
|
||||
const correlationData: CorrelationData[] = baseData.map((bar, idx) => {
|
||||
prices.spy *= 1 + returns[idx].spy;
|
||||
prices.qqq *= 1 + returns[idx].qqq;
|
||||
prices.iwm *= 1 + returns[idx].iwm;
|
||||
prices.vix *= 1 + returns[idx].vix;
|
||||
prices.dxy *= 1 + returns[idx].dxy;
|
||||
|
||||
// Calculate rolling correlation (simplified)
|
||||
const window = 20;
|
||||
const start = Math.max(0, idx - window);
|
||||
const spyReturns = returns.slice(start, idx + 1).map((r) => r.spy);
|
||||
const qqqReturns = returns.slice(start, idx + 1).map((r) => r.qqq);
|
||||
const vixReturns = returns.slice(start, idx + 1).map((r) => r.vix);
|
||||
|
||||
const correlation = (arr1: number[], arr2: number[]): number => {
|
||||
const n = arr1.length;
|
||||
const mean1 = arr1.reduce((a, b) => a + b, 0) / n;
|
||||
const mean2 = arr2.reduce((a, b) => a + b, 0) / n;
|
||||
|
||||
const numerator = arr1.reduce(
|
||||
(sum, val, i) => sum + (val - mean1) * (arr2[i] - mean2),
|
||||
0
|
||||
);
|
||||
const denom1 = Math.sqrt(
|
||||
arr1.reduce((sum, val) => sum + Math.pow(val - mean1, 2), 0)
|
||||
);
|
||||
const denom2 = Math.sqrt(
|
||||
arr2.reduce((sum, val) => sum + Math.pow(val - mean2, 2), 0)
|
||||
);
|
||||
|
||||
return numerator / (denom1 * denom2);
|
||||
};
|
||||
|
||||
return {
|
||||
timestamp: bar.timestamp,
|
||||
spy: Number(prices.spy.toFixed(2)),
|
||||
qqq: Number(prices.qqq.toFixed(2)),
|
||||
iwm: Number(prices.iwm.toFixed(2)),
|
||||
vix: Number(prices.vix.toFixed(2)),
|
||||
dxy: Number(prices.dxy.toFixed(2)),
|
||||
correlation_spy_qqq: Number(correlation(spyReturns, qqqReturns).toFixed(4)),
|
||||
correlation_spy_vix: Number(correlation(spyReturns, vixReturns).toFixed(4)),
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Market Correlation Data:');
|
||||
console.log({
|
||||
avgCorrelation_SPY_QQQ:
|
||||
correlationData.reduce((sum, d) => sum + d.correlation_spy_qqq, 0) /
|
||||
correlationData.length,
|
||||
avgCorrelation_SPY_VIX:
|
||||
correlationData.reduce((sum, d) => sum + d.correlation_spy_vix, 0) /
|
||||
correlationData.length,
|
||||
});
|
||||
|
||||
return correlationData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main Execution
|
||||
// ============================================================================
|
||||
|
||||
async function main() {
|
||||
console.log('='.repeat(80));
|
||||
console.log('Trading Scenarios Generation');
|
||||
console.log('='.repeat(80));
|
||||
console.log();
|
||||
|
||||
try {
|
||||
console.log('1. Generating Bull Market Scenario...');
|
||||
await generateBullMarket();
|
||||
console.log();
|
||||
|
||||
console.log('2. Generating Bear Market Scenario...');
|
||||
await generateBearMarket();
|
||||
console.log();
|
||||
|
||||
console.log('3. Generating Volatility Patterns...');
|
||||
await generateVolatilityPatterns();
|
||||
console.log();
|
||||
|
||||
console.log('4. Generating Flash Crash...');
|
||||
await generateFlashCrash();
|
||||
console.log();
|
||||
|
||||
console.log('5. Generating Earnings Scenario...');
|
||||
await generateEarningsScenario();
|
||||
console.log();
|
||||
|
||||
console.log('6. Generating Correlated Markets...');
|
||||
await generateCorrelatedMarkets();
|
||||
console.log();
|
||||
|
||||
console.log('All trading scenarios generated successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error generating trading scenarios:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
export {
|
||||
generateBullMarket,
|
||||
generateBearMarket,
|
||||
generateVolatilityPatterns,
|
||||
generateFlashCrash,
|
||||
generateEarningsScenario,
|
||||
generateCorrelatedMarkets,
|
||||
};
|
||||
Reference in New Issue
Block a user