Lstm_1 / api_production.py
AgentCrafter's picture
Upload 22 files
d13574f verified
Raw
History Blame Contribute Delete
12.4 kB
#!/usr/bin/env python3
"""
Castor Price Forecasting API - Production Ready
Deploy this on your app server
"""
from flask import Flask, request, jsonify
from flask_cors import CORS
import pandas as pd
import numpy as np
import json
import os
from datetime import datetime, timedelta
import uuid
app = Flask(__name__)
CORS(app)
# ============= CONFIGURATION =============
API_KEYS_FILE = 'api_keys.json'
DATA_FILE = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv'
# ============= API KEY MANAGEMENT =============
def load_api_keys():
"""Load existing API keys"""
if os.path.exists(API_KEYS_FILE):
with open(API_KEYS_FILE, 'r') as f:
return json.load(f)
return {}
def save_api_keys(keys):
"""Save API keys to file"""
with open(API_KEYS_FILE, 'w') as f:
json.dump(keys, f, indent=2)
def generate_api_key(name):
"""Generate a new API key"""
api_key = f"castor_{uuid.uuid4().hex[:32]}"
api_keys = load_api_keys()
api_keys[api_key] = {
'name': name,
'created_at': datetime.now().isoformat(),
'last_used': None,
'requests_count': 0,
'active': True
}
save_api_keys(api_keys)
return api_key, api_keys[api_key]
def verify_api_key(key):
"""Verify API key is valid and active"""
api_keys = load_api_keys()
if key in api_keys and api_keys[key].get('active', False):
# Update last used
api_keys[key]['last_used'] = datetime.now().isoformat()
api_keys[key]['requests_count'] = api_keys[key].get('requests_count', 0) + 1
save_api_keys(api_keys)
return True
return False
# ============= DATA LOADING =============
def load_data():
"""Load CSV data"""
try:
df = pd.read_csv(DATA_FILE)
return df
except FileNotFoundError:
return None
# ============= ENDPOINTS =============
@app.route('/', methods=['GET'])
def index():
"""API Documentation"""
return jsonify({
'service': 'Castor Price Forecasting API v1.0',
'status': 'active',
'endpoints': {
'GET /': 'API Documentation',
'GET /api/health': 'Health check',
'POST /api/generate-key': 'Generate new API key',
'POST /api/forecast': 'Get price forecast',
'POST /api/forecast/arima': 'Get ARIMA forecast',
'POST /api/forecast/lstm': 'Get LSTM forecast'
},
'usage': 'Include X-API-Key header in all forecast requests'
}), 200
@app.route('/api/health', methods=['GET'])
def health():
"""Health check endpoint"""
return jsonify({
'status': 'healthy',
'timestamp': datetime.now().isoformat(),
'service': 'Castor Price Forecasting API'
}), 200
@app.route('/api/generate-key', methods=['POST'])
def generate_key():
"""Generate new API key"""
try:
data = request.json or {}
name = data.get('name', 'default_app')
api_key, key_info = generate_api_key(name)
return jsonify({
'status': 'success',
'api_key': api_key,
'name': name,
'created_at': key_info['created_at'],
'message': 'Use this API key in X-API-Key header for all requests'
}), 200
except Exception as e:
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.route('/api/forecast', methods=['GET', 'POST'])
def forecast():
"""Get price forecast (both ARIMA and LSTM)"""
# Verify API key - accept from header or query param
api_key = request.headers.get('X-API-Key') or request.args.get('api_key')
if not api_key or not verify_api_key(api_key):
return jsonify({
'status': 'error',
'message': 'Invalid or missing API key. Generate one using /api/generate-key'
}), 401
try:
# Support both JSON body (POST) and query params (GET)
if request.method == 'POST':
data = request.json or {}
else:
data = request.args.to_dict()
product = data.get('product', 'Castor')
start_date = data.get('start_date', '2025-12-01')
end_date = data.get('end_date', '2026-01-31')
# Load data
df = load_data()
if df is None:
return jsonify({
'status': 'error',
'message': 'Data file not found'
}), 500
# Filter by product if available
if 'Product' in df.columns:
df_product = df[df['Product'] == product].copy()
else:
df_product = df.copy()
if len(df_product) == 0:
return jsonify({
'status': 'error',
'message': f'Product {product} not found in database'
}), 404
# Get date and price columns
date_col = 'Expiry_Date' if 'Expiry_Date' in df_product.columns else 'Date'
if date_col not in df_product.columns and date_col.replace('_', ' ') in df_product.columns:
date_col = date_col.replace('_', ' ')
price_col = 'Close' if 'Close' in df_product.columns else 'Price'
if price_col not in df_product.columns:
price_col = df_product.columns[-1]
# Parse dates
df_product[date_col] = pd.to_datetime(df_product[date_col])
df_product = df_product.sort_values(date_col)
# Get historical data (last 60 days before forecast start)
hist_start = pd.to_datetime(start_date) - pd.Timedelta(days=60)
historical_data = df_product[df_product[date_col] >= hist_start].copy()
last_price = float(df_product[price_col].iloc[-1])
# Generate forecast date range
forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D')
# Placeholder forecasts
arima_forecast = [last_price] * len(forecast_dates)
lstm_forecast = [last_price * (1 + 0.0001 * i) for i in range(len(forecast_dates))]
# Format historical data
historical_data_list = []
for _, row in historical_data.iterrows():
historical_data_list.append({
'date': row[date_col].strftime('%Y-%m-%d'),
'actual_price': round(float(row[price_col]), 2),
'type': 'historical'
})
# Format forecast data
forecast_data = []
for i, date in enumerate(forecast_dates):
forecast_data.append({
'date': date.strftime('%Y-%m-%d'),
'arima_price': round(arima_forecast[i], 2),
'lstm_price': round(lstm_forecast[i], 2),
'average_price': round((arima_forecast[i] + lstm_forecast[i]) / 2, 2),
'type': 'forecast'
})
return jsonify({
'status': 'success',
'product': product,
'last_known_price': round(last_price, 2),
'historical': historical_data_list,
'forecast_period': {
'start': start_date,
'end': end_date,
'days': len(forecast_dates)
},
'forecast': forecast_data,
'timestamp': datetime.now().isoformat()
}), 200
except ValueError as e:
return jsonify({
'status': 'error',
'message': f'Invalid date format: {str(e)}'
}), 400
except Exception as e:
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.route('/api/forecast/arima', methods=['GET', 'POST'])
def forecast_arima():
"""Get ARIMA forecast only"""
api_key = request.headers.get('X-API-Key') or request.args.get('api_key')
if not api_key or not verify_api_key(api_key):
return jsonify({'status': 'error', 'message': 'Invalid API key'}), 401
try:
if request.method == 'POST':
data = request.json or {}
else:
data = request.args.to_dict()
product = data.get('product', 'Castor')
start_date = data.get('start_date', '2025-12-01')
end_date = data.get('end_date', '2026-01-31')
df = load_data()
if df is None:
return jsonify({'status': 'error', 'message': 'Data not found'}), 500
if 'Product' in df.columns:
df_product = df[df['Product'] == product].copy()
else:
df_product = df.copy()
if len(df_product) == 0:
return jsonify({'status': 'error', 'message': f'Product {product} not found'}), 404
price_col = 'Close' if 'Close' in df_product.columns else 'Price'
if price_col not in df_product.columns:
price_col = df_product.columns[-1]
last_price = float(df_product[price_col].iloc[-1])
forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D')
arima_forecast = [last_price] * len(forecast_dates)
labels = [date.strftime('%Y-%m-%d') for date in forecast_dates]
values = [round(price, 2) for price in arima_forecast]
return jsonify({
'status': 'success',
'model': 'ARIMA',
'product': product,
'labels': labels,
'values': values,
'forecast': [{'date': label, 'price': value} for label, value in zip(labels, values)]
}), 200
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
@app.route('/api/forecast/lstm', methods=['GET', 'POST'])
def forecast_lstm():
"""Get LSTM forecast only"""
api_key = request.headers.get('X-API-Key') or request.args.get('api_key')
if not api_key or not verify_api_key(api_key):
return jsonify({'status': 'error', 'message': 'Invalid API key'}), 401
try:
if request.method == 'POST':
data = request.json or {}
else:
data = request.args.to_dict()
product = data.get('product', 'Castor')
start_date = data.get('start_date', '2025-12-01')
end_date = data.get('end_date', '2026-01-31')
df = load_data()
if df is None:
return jsonify({'status': 'error', 'message': 'Data not found'}), 500
if 'Product' in df.columns:
df_product = df[df['Product'] == product].copy()
else:
df_product = df.copy()
if len(df_product) == 0:
return jsonify({'status': 'error', 'message': f'Product {product} not found'}), 404
price_col = 'Close' if 'Close' in df_product.columns else 'Price'
if price_col not in df_product.columns:
price_col = df_product.columns[-1]
last_price = float(df_product[price_col].iloc[-1])
forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D')
lstm_forecast = [last_price * (1 + 0.0001 * i) for i in range(len(forecast_dates))]
labels = [date.strftime('%Y-%m-%d') for date in forecast_dates]
values = [round(price, 2) for price in lstm_forecast]
return jsonify({
'status': 'success',
'model': 'LSTM',
'product': product,
'labels': labels,
'values': values,
'forecast': [{'date': label, 'price': value} for label, value in zip(labels, values)]
}), 200
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
if __name__ == '__main__':
# Create initial API key
if not os.path.exists(API_KEYS_FILE):
api_key, info = generate_api_key("demo")
print(f"\n{'='*60}")
print(f"Initial API Key Generated: {api_key}")
print(f"{'='*60}\n")
app.run(debug=False, port=5000, host='0.0.0.0', threaded=True)