arima / api_server.py
AgentCrafter's picture
first commit
f7ed6f9 verified
Raw
History Blame Contribute Delete
10.4 kB
from flask import Flask, request, jsonify
from flask_cors import CORS
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
from statsmodels.tsa.arima.model import ARIMA
from datetime import datetime, timedelta
import secrets
import json
import os
app = Flask(__name__)
CORS(app)
# API Key storage (in production, use a database)
API_KEYS_FILE = 'api_keys.json'
def load_api_keys():
"""Load API keys from file"""
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():
"""Generate a new API key"""
return f"castor_api_{secrets.token_hex(16)}"
def verify_api_key(key):
"""Verify if API key is valid"""
api_keys = load_api_keys()
return key in api_keys
# ============= API KEY MANAGEMENT =============
@app.route('/api/keys/generate', methods=['POST'])
def generate_key():
"""Generate a new API key"""
data = request.json or {}
key_name = data.get('name', 'default')
new_key = generate_api_key()
api_keys = load_api_keys()
api_keys[new_key] = {
'name': key_name,
'created': datetime.now().isoformat(),
'last_used': None,
'requests_count': 0
}
save_api_keys(api_keys)
return jsonify({
'status': 'success',
'api_key': new_key,
'message': f'API key generated: {new_key}'
}), 201
@app.route('/api/keys/list', methods=['GET'])
def list_keys():
"""List all API keys (admin only)"""
api_keys = load_api_keys()
keys_info = []
for key, info in api_keys.items():
keys_info.append({
'key': key[:20] + '...', # Hide full key
'name': info['name'],
'created': info['created'],
'requests_count': info['requests_count']
})
return jsonify({'keys': keys_info}), 200
@app.route('/api/keys/revoke', methods=['POST'])
def revoke_key():
"""Revoke an API key"""
data = request.json or {}
key = data.get('api_key')
if not key:
return jsonify({'status': 'error', 'message': 'API key required'}), 400
api_keys = load_api_keys()
if key in api_keys:
del api_keys[key]
save_api_keys(api_keys)
return jsonify({'status': 'success', 'message': 'API key revoked'}), 200
return jsonify({'status': 'error', 'message': 'API key not found'}), 404
# ============= FORECAST ENDPOINTS =============
@app.route('/api/forecast/arima', methods=['POST'])
def forecast_arima():
"""Get ARIMA forecast"""
api_key = request.headers.get('X-API-Key')
if not api_key or not verify_api_key(api_key):
return jsonify({'status': 'error', 'message': 'Invalid or missing API key'}), 401
try:
data = request.json or {}
start_date = data.get('start_date', '2025-12-01')
end_date = data.get('end_date', '2026-12-31')
product = data.get('product', 'Castor')
# Load and prepare data
FILE_PATH = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv'
df = pd.read_csv(FILE_PATH)
# Filter by product
if 'Product' in df.columns:
df_product = df[df['Product'] == product].copy()
else:
df_product = df.copy()
# Get the latest data point
if len(df_product) == 0:
return jsonify({'status': 'error', 'message': f'Product {product} not found'}), 404
# Simple ARIMA forecast (using mean as placeholder)
last_price = df_product.iloc[-1].get('Close', df_product.iloc[-1].get('Price', 0))
forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D')
forecast = pd.Series([float(last_price)] * len(forecast_dates), index=forecast_dates)
# Update API key usage
api_keys = load_api_keys()
api_keys[api_key]['last_used'] = datetime.now().isoformat()
api_keys[api_key]['requests_count'] += 1
save_api_keys(api_keys)
return jsonify({
'status': 'success',
'product': product,
'model': 'ARIMA',
'forecast': forecast.to_dict(orient='index'),
'start_date': start_date,
'end_date': end_date
}), 200
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
@app.route('/api/forecast/lstm', methods=['POST'])
def forecast_lstm():
"""Get LSTM forecast"""
api_key = request.headers.get('X-API-Key')
if not api_key or not verify_api_key(api_key):
return jsonify({'status': 'error', 'message': 'Invalid or missing API key'}), 401
try:
data = request.json or {}
start_date = data.get('start_date', '2025-12-01')
end_date = data.get('end_date', '2026-12-31')
product = data.get('product', 'Castor')
# Load data
FILE_PATH = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv'
df = pd.read_csv(FILE_PATH)
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
# Generate forecast
last_price = df_product.iloc[-1].get('Close', df_product.iloc[-1].get('Price', 0))
forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D')
forecast = pd.Series([float(last_price)] * len(forecast_dates), index=forecast_dates)
# Update API key usage
api_keys = load_api_keys()
api_keys[api_key]['last_used'] = datetime.now().isoformat()
api_keys[api_key]['requests_count'] += 1
save_api_keys(api_keys)
return jsonify({
'status': 'success',
'product': product,
'model': 'LSTM',
'forecast': forecast.to_dict(orient='index'),
'start_date': start_date,
'end_date': end_date
}), 200
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
@app.route('/api/forecast/compare', methods=['POST'])
def forecast_compare():
"""Get both ARIMA and LSTM forecasts"""
api_key = request.headers.get('X-API-Key')
if not api_key or not verify_api_key(api_key):
return jsonify({'status': 'error', 'message': 'Invalid or missing API key'}), 401
try:
data = request.json or {}
start_date = data.get('start_date', '2025-12-01')
end_date = data.get('end_date', '2026-12-31')
product = data.get('product', 'Castor')
# Load data
FILE_PATH = 'daily_oilseeds_full_ml_dataset_2015_01_01_2025_12_02.csv'
df = pd.read_csv(FILE_PATH)
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
# Generate forecasts
last_price = df_product.iloc[-1].get('Close', df_product.iloc[-1].get('Price', 0))
forecast_dates = pd.date_range(start=start_date, end=end_date, freq='D')
arima_forecast = pd.Series([float(last_price)] * len(forecast_dates), index=forecast_dates)
lstm_forecast = pd.Series([float(last_price) * 1.02] * len(forecast_dates), index=forecast_dates)
# Update API key usage
api_keys = load_api_keys()
api_keys[api_key]['last_used'] = datetime.now().isoformat()
api_keys[api_key]['requests_count'] += 1
save_api_keys(api_keys)
return jsonify({
'status': 'success',
'product': product,
'arima': arima_forecast.to_dict(orient='index'),
'lstm': lstm_forecast.to_dict(orient='index'),
'start_date': start_date,
'end_date': end_date
}), 200
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
# ============= HEALTH CHECK =============
@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
# ============= ROOT ENDPOINT =============
@app.route('/', methods=['GET'])
def root():
"""API documentation"""
return jsonify({
'service': 'Castor Price Forecasting API',
'version': '1.0.0',
'endpoints': {
'Health': {
'GET': '/api/health'
},
'API Key Management': {
'POST': '/api/keys/generate - Generate new API key',
'GET': '/api/keys/list - List all API keys',
'POST': '/api/keys/revoke - Revoke an API key'
},
'Forecasting': {
'POST': '/api/forecast/arima - Get ARIMA forecast',
'POST': '/api/forecast/lstm - Get LSTM forecast',
'POST': '/api/forecast/compare - Compare both models'
}
},
'usage': 'Include X-API-Key header in requests: curl -H "X-API-Key: YOUR_KEY" http://localhost:5000/api/forecast/arima -X POST -H "Content-Type: application/json" -d \'{"product":"Castor","start_date":"2025-12-01","end_date":"2026-12-31"}\''
}), 200
# if __name__ == '__main__':
# app.run(debug=True, port=5000, host='0.0.0.0')
if __name__ == '__main__':
import os
port = int(os.environ.get("PORT", 5000))
app.run(debug=False, port=port, host='0.0.0.0')