- Replace mock data with real market analysis in paper trading - Safe paper trading API now uses live TradingView screenshots and OpenAI analysis - Maintain complete isolation from live trading while using real market conditions - Fix Docker build error in automation trade route (removed unreachable code) - Add safety redirects to prevent accidental live trading access - Real data includes: live charts, technical indicators, current market conditions - Analysis time: 30-180s for genuine market analysis vs 5s for mock data - All safety blocks maintained for zero trading risk learning environment Tested and verified: Container builds and runs successfully Real screenshot capture working (TradingView integration) OpenAI analysis processing real market data Safety systems prevent any actual trading Paper trading provides realistic learning experience
166 lines
5.1 KiB
TypeScript
166 lines
5.1 KiB
TypeScript
/**
|
|
* Cost-Effective Paper Trading Configuration
|
|
*
|
|
* IMPORTANT: This configuration prevents automatic recurring AI analysis
|
|
* to avoid expensive OpenAI API costs. Analysis only runs when manually triggered.
|
|
*/
|
|
|
|
export const PAPER_TRADING_CONFIG = {
|
|
// Cost Control Settings
|
|
costControl: {
|
|
autoRunDisabled: true, // NEVER auto-run AI analysis
|
|
maxAnalysisPerHour: 5, // Limit to 5 analyses per hour max
|
|
manualTriggerOnly: true, // Only run when user clicks button
|
|
timeframeCooldown: 300000, // 5 minutes between same-symbol analyses
|
|
},
|
|
|
|
// User's Selected Timeframes
|
|
selectedTimeframes: [
|
|
{
|
|
label: '5m',
|
|
value: '5',
|
|
riskLevel: 'HIGH',
|
|
description: 'High-frequency scalping (use sparingly)',
|
|
maxAnalysisPerDay: 10 // Very limited for 5m
|
|
},
|
|
{
|
|
label: '30m',
|
|
value: '30',
|
|
riskLevel: 'MEDIUM',
|
|
description: 'Medium-term swing trading',
|
|
maxAnalysisPerDay: 20
|
|
},
|
|
{
|
|
label: '1h',
|
|
value: '60',
|
|
riskLevel: 'MEDIUM',
|
|
description: 'Hourly trend analysis',
|
|
maxAnalysisPerDay: 15
|
|
},
|
|
{
|
|
label: '4h',
|
|
value: '240',
|
|
riskLevel: 'LOW',
|
|
description: 'Daily trend analysis (recommended)',
|
|
maxAnalysisPerDay: 8
|
|
}
|
|
],
|
|
|
|
// Cost Estimation per Analysis
|
|
costEstimation: {
|
|
openaiCostPerAnalysis: 0.006, // ~$0.006 per screenshot analysis
|
|
dailyBudgetRecommended: 0.50, // $0.50 per day recommended
|
|
monthlyBudgetEstimate: 15.00, // $15/month for reasonable usage
|
|
},
|
|
|
|
// Paper Trading Settings
|
|
paperTrading: {
|
|
startingBalance: 1000,
|
|
riskPerTrade: 1.0, // 1% max risk per trade
|
|
maxConcurrentTrades: 3,
|
|
stopLossRequired: true,
|
|
minConfidenceRequired: 75, // High confidence required for paper trades
|
|
},
|
|
|
|
// Anti-Chasing Settings (your specific requirements)
|
|
antiChasing: {
|
|
enabled: true,
|
|
momentumExhaustionDetection: true,
|
|
multiTimeframeValidation: true,
|
|
preventChasing: true,
|
|
minQualityScore: 70, // Entry quality must be 70/100+
|
|
}
|
|
}
|
|
|
|
// Usage tracking to prevent excessive API calls
|
|
export class PaperTradingUsageTracker {
|
|
private static instance: PaperTradingUsageTracker
|
|
private analysisCount: Map<string, number> = new Map()
|
|
private lastAnalysis: Map<string, number> = new Map()
|
|
private dailyCount = 0
|
|
private lastReset = new Date().getDate()
|
|
|
|
static getInstance(): PaperTradingUsageTracker {
|
|
if (!PaperTradingUsageTracker.instance) {
|
|
PaperTradingUsageTracker.instance = new PaperTradingUsageTracker()
|
|
}
|
|
return PaperTradingUsageTracker.instance
|
|
}
|
|
|
|
canRunAnalysis(symbol: string, timeframe: string): { allowed: boolean, reason?: string } {
|
|
// Reset daily count if new day
|
|
const currentDay = new Date().getDate()
|
|
if (currentDay !== this.lastReset) {
|
|
this.dailyCount = 0
|
|
this.lastReset = currentDay
|
|
this.analysisCount.clear()
|
|
}
|
|
|
|
const key = `${symbol}_${timeframe}`
|
|
const now = Date.now()
|
|
const lastRun = this.lastAnalysis.get(key) || 0
|
|
const timeSinceLastRun = now - lastRun
|
|
|
|
// Check cooldown period (5 minutes)
|
|
if (timeSinceLastRun < PAPER_TRADING_CONFIG.costControl.timeframeCooldown) {
|
|
const remainingTime = Math.ceil((PAPER_TRADING_CONFIG.costControl.timeframeCooldown - timeSinceLastRun) / 1000 / 60)
|
|
return {
|
|
allowed: false,
|
|
reason: `Cooldown active. Wait ${remainingTime} minutes before analyzing ${symbol} ${timeframe} again.`
|
|
}
|
|
}
|
|
|
|
// Check hourly limit
|
|
const hourlyCount = this.getHourlyCount()
|
|
if (hourlyCount >= PAPER_TRADING_CONFIG.costControl.maxAnalysisPerHour) {
|
|
return {
|
|
allowed: false,
|
|
reason: `Hourly limit reached (${PAPER_TRADING_CONFIG.costControl.maxAnalysisPerHour} analyses/hour). Wait for next hour.`
|
|
}
|
|
}
|
|
|
|
// Check daily limit for timeframe
|
|
const timeframeConfig = PAPER_TRADING_CONFIG.selectedTimeframes.find(tf => tf.value === timeframe)
|
|
const dailyLimit = timeframeConfig?.maxAnalysisPerDay || 10
|
|
const dailyCountForTimeframe = this.analysisCount.get(timeframe) || 0
|
|
|
|
if (dailyCountForTimeframe >= dailyLimit) {
|
|
return {
|
|
allowed: false,
|
|
reason: `Daily limit reached for ${timeframe} timeframe (${dailyLimit} analyses/day).`
|
|
}
|
|
}
|
|
|
|
return { allowed: true }
|
|
}
|
|
|
|
recordAnalysis(symbol: string, timeframe: string): void {
|
|
const key = `${symbol}_${timeframe}`
|
|
this.lastAnalysis.set(key, Date.now())
|
|
this.analysisCount.set(timeframe, (this.analysisCount.get(timeframe) || 0) + 1)
|
|
this.dailyCount++
|
|
}
|
|
|
|
private getHourlyCount(): number {
|
|
const oneHourAgo = Date.now() - (60 * 60 * 1000)
|
|
let count = 0
|
|
|
|
for (const [, timestamp] of this.lastAnalysis) {
|
|
if (timestamp > oneHourAgo) {
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|
|
|
|
getUsageStats() {
|
|
return {
|
|
dailyCount: this.dailyCount,
|
|
estimatedDailyCost: this.dailyCount * PAPER_TRADING_CONFIG.costEstimation.openaiCostPerAnalysis,
|
|
hourlyCount: this.getHourlyCount(),
|
|
timeframeCounts: Object.fromEntries(this.analysisCount)
|
|
}
|
|
}
|
|
}
|