0% found this document useful (0 votes)
9 views136 pages

ALGOblocks 3

The document outlines the structure and functionality of a Flask application named ALGOblocks, which includes routes for user authentication, portfolio management, and trading strategies. It utilizes Alpaca's trading API for market data and trading operations, and incorporates features like user registration, login, strategy saving, and backtesting. The application is organized into various directories for data, templates, static files, and utility functions, with a focus on user experience and security.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views136 pages

ALGOblocks 3

The document outlines the structure and functionality of a Flask application named ALGOblocks, which includes routes for user authentication, portfolio management, and trading strategies. It utilizes Alpaca's trading API for market data and trading operations, and incorporates features like user registration, login, strategy saving, and backtesting. The application is organized into various directories for data, templates, static files, and utility functions, with a focus on user experience and security.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 136

ALGOblocks/ ├── app.py ├── .env ├── requirements.

txt ├── data/ │ ├──


historical_data.csv │ ├── saved_strategies/ │ └── users.json ├── static/ │ ├── css/ │ │ ├
── style.css │ │ └── auth.css │ └── js/ │ │ │ ├── script.js ├── portfolio.js └── tutorial.js ├
── templates/ │ ├── index.html │ ├── login.html │ ├── register.html │ ├── verify.html │
├── portfolio.html │ └── tutorial.html ├── tutorials/ │ ├── __init__.py │ ├──
content.json │ └── terms.json └── utils/ ├── __init__.py ├── auth_utils.py ├──
backtest_engine.py ├── data_fetcher.py ├── strategy_parser.py └── trade_manager.py

import os

import json

import time

from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session

from dotenv import load_dotenv

import alpaca_trade_api as tradeapi

from utils.backtest_engine import BacktestEngine

from utils.data_fetcher import DataFetcher

from utils.strategy_parser import StrategyParser

from utils.auth_utils import register_user, verify_user, login_user, logout_user

from utils.trade_manager import save_paper_trade, get_user_portfolio

# Load environment variables

load_dotenv()

app = Flask(__name__)

app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'your_default_secret_key')

# Ensure directories exist

os.makedirs('data/saved_strategies', exist_ok=True)

os.chmod('data/saved_strategies', 0o755) # Read/write/execute for owner, read/execute for


others
ALPACA_API_KEY = os.getenv('APCA_API_KEY_ID')

ALPACA_SECRET_KEY = os.getenv('APCA_API_SECRET_KEY')

api = tradeapi.REST(ALPACA_API_KEY, ALPACA_SECRET_KEY, base_url='https://paper-


api.alpaca.markets')

data_fetcher = DataFetcher(api)

backtest_engine = BacktestEngine(data_fetcher)

# --- AUTH ROUTES ---

@app.route('/login', methods=['GET', 'POST'])

def login():

if 'user_email' in session:

return redirect(url_for('index'))

if request.method == 'POST':

email = request.form.get('email')

password = request.form.get('password')

success, message = login_user(email, password)

if success:

return redirect(url_for('index'))

else:

flash(message, 'danger')

return render_template('login.html')

@app.route('/register', methods=['GET', 'POST'])

def register():

if 'user_email' in session:

return redirect(url_for('index'))

if request.method == 'POST':

email = request.form.get('email')
password = request.form.get('password')

username = request.form.get('username')

success, message = register_user(email, password, username)

if success:

flash('Registration successful! Please check your email for verification.', 'success')

return redirect(url_for('verify'))

else:

flash(message, 'danger')

return render_template('register.html')

@app.route('/verify', methods=['GET', 'POST'])

def verify():

if 'user_email' in session:

return redirect(url_for('index'))

if request.method == 'POST':

email = request.form.get('email')

code = request.form.get('code')

success, message = verify_user(email, code)

if success:

flash('Email verified! You can now log in.', 'success')

return redirect(url_for('login'))

else:

flash(message, 'danger')

return render_template('verify.html')

@app.route('/logout')

def logout():

logout_user()
flash('You have been logged out.', 'info')

return redirect(url_for('login'))

# --- MAIN ROUTES ---

@app.route('/')

def index():

if 'user_email' not in session:

return redirect(url_for('login'))

return render_template('index.html')

@app.route('/portfolio')

def portfolio():

if 'user_email' not in session:

return redirect(url_for('login'))

email = session['user_email']

portfolio_data = get_user_portfolio(email)

if not portfolio_data:

flash('Error loading portfolio.', 'danger')

return redirect(url_for('index'))

return render_template('portfolio.html', portfolio=portfolio_data)

@app.route('/tutorial')

def tutorial():

if 'user_email' not in session:

return redirect(url_for('login'))

return render_template('tutorial.html')

# --- TUTORIAL API ROUTES ---


@app.route('/api/tutorial/content')

def tutorial_content():

with open('tutorials/content.json', 'r', encoding='utf-8') as f:

return jsonify(json.load(f))

@app.route('/api/tutorial/terms')

def tutorial_terms():

with open('tutorials/terms.json', 'r', encoding='utf-8') as f:

return jsonify(json.load(f))

# --- API ROUTES (FIXED) ---

@app.route('/api/markets', methods=['GET'])

def get_market_status():

try:

clock = api.get_clock()

return jsonify({

'is_open': clock.is_open,

'next_open': clock.next_open.isoformat(),

'next_close': clock.next_close.isoformat()

})

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/symbols', methods=['GET'])

def get_available_symbols():

try:

page = int(request.args.get('page', 1))

per_page = int(request.args.get('per_page', 50))


assets = api.list_assets(status='active')

symbols = [asset.symbol for asset in assets if asset.tradable and asset.asset_class ==


'us_equity']

# Calculate pagination

start_idx = (page - 1) * per_page

end_idx = start_idx + per_page

# If requested page exceeds total pages, return last page

if start_idx >= len(symbols):

page = (len(symbols) + per_page - 1) // per_page

start_idx = (page - 1) * per_page

end_idx = start_idx + per_page

paginated_symbols = symbols[start_idx:min(end_idx, len(symbols))]

return jsonify({

'symbols': paginated_symbols,

'total': len(symbols),

'page': page,

'pages': (len(symbols) + per_page - 1) // per_page

})

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/search-symbols', methods=['GET'])

def search_symbols():

query = request.args.get('query', '').upper()


try:

assets = api.list_assets(status='active')

symbols = [

{"symbol": asset.symbol, "name": asset.name}

for asset in assets

if asset.tradable and asset.asset_class == 'us_equity' and

(query in asset.symbol or (asset.name and query in asset.name.upper()))

# Implement pagination for search results

page = int(request.args.get('page', 1))

per_page = int(request.args.get('per_page', 50))

start_idx = (page - 1) * per_page

end_idx = start_idx + per_page

paginated_symbols = symbols[start_idx:min(end_idx, len(symbols))]

return jsonify({

'symbols': paginated_symbols,

'total': len(symbols),

'page': page,

'pages': (len(symbols) + per_page - 1) // per_page

})

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/historical-data', methods=['GET'])

def get_historical_data():
symbol = request.args.get('symbol', 'AAPL')

timeframe = request.args.get('timeframe', '1D')

period = request.args.get('period', '1M')

try:

data = data_fetcher.get_historical_data(symbol, timeframe, period)

return jsonify(data)

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/account', methods=['GET'])

def get_account():

try:

account = api.get_account()

return jsonify({

'cash': float(account.cash),

'equity': float(account.equity),

'buying_power': float(account.buying_power),

'portfolio_value': float(account.portfolio_value),

'status': account.status

})

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/positions', methods=['GET'])

def get_positions():

try:

positions = api.list_positions()

formatted_positions = []
for position in positions:

formatted_positions.append({

'symbol': position.symbol,

'qty': position.qty,

'avg_entry_price': position.avg_entry_price,

'current_price': position.current_price,

'market_value': position.market_value,

'unrealized_pl': position.unrealized_pl,

'unrealized_plpc': position.unrealized_plpc

})

return jsonify(formatted_positions)

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/backtest', methods=['POST'])

def run_backtest():

if 'user_email' not in session:

return jsonify({"error": "Please log in to run a backtest"}), 401

try:

if not request.is_json:

return jsonify({"error": "Invalid content type, JSON required"}), 400

strategy_config = request.get_json()

symbol = strategy_config.get('symbol', 'AAPL')

start_date = strategy_config.get('startDate', '2024-01-01')

end_date = strategy_config.get('endDate', '2025-04-19')

initial_capital = float(strategy_config.get('capital', 10000))

blocks = strategy_config.get('blocks')
if isinstance(blocks, dict) and 'indicators' in blocks:

strategy = blocks

else:

parser = StrategyParser(blocks)

strategy = parser.parse_blocks()

results = backtest_engine.run_backtest(

strategy=strategy,

symbol=symbol,

start_date=start_date,

end_date=end_date,

initial_capital=initial_capital

return jsonify(results)

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/paper-trade', methods=['POST'])

def submit_paper_trade():

if 'user_email' not in session:

return jsonify({'error': 'Please log in to paper trade'}), 401

if not request.is_json:

return jsonify({'error': 'Invalid request format'}), 400

email = session['user_email']

trade_data = request.json

success, result = save_paper_trade(email, trade_data)

if success:

return jsonify({
'success': True,

'message': 'Trade executed successfully',

'data': result

})

else:

return jsonify({'error': result}), 400

@app.route('/api/portfolio/update', methods=['GET'])

def update_portfolio():

if 'user_email' not in session:

return jsonify({'error': 'Not logged in'}), 401

email = session['user_email']

portfolio_data = get_user_portfolio(email)

if not portfolio_data:

return jsonify({'error': 'Failed to load portfolio'}), 400

return jsonify({

'success': True,

'portfolio': portfolio_data

})

@app.route('/api/save-strategy', methods=['POST'])

def save_strategy():

if 'user_email' not in session:

return jsonify({"error": "Please log in to save a strategy"}), 401

try:

if not request.is_json:

return jsonify({"error": "Invalid content type, JSON required"}), 400

strategy_data = request.get_json()
if 'blocks' not in strategy_data or not strategy_data['blocks']:

return jsonify({"error": "Strategy blocks are required"}), 400

strategy_data['user_email'] = session['user_email']

strategy_data['username'] = session.get('username', 'Anonymous')

strategy_name = strategy_data.get('name', f"strategy_{int(time.time())}")

strategy_name = ''.join(c for c in strategy_name if c.isalnum() or c in '._- ')

save_dir = 'data/saved_strategies'

os.makedirs(save_dir, exist_ok=True) # Ensure directory exists

filepath = os.path.join(save_dir, f"{strategy_name}.json")

try:

with open(filepath, 'w') as f:

json.dump(strategy_data, f, indent=4)

except PermissionError:

return jsonify({"error": "Permission denied saving strategy"}), 403

except IOError:

return jsonify({"error": "IO error saving strategy"}), 500

return jsonify({

"success": True,

"message": f"Strategy saved as {strategy_name}",

"filename": f"{strategy_name}.json"

})

except Exception as e:

return jsonify({'error': str(e)}), 500


@app.route('/api/list-strategies', methods=['GET'])

def list_strategies():

if 'user_email' not in session:

return jsonify({"error": "Please log in to view strategies"}), 401

try:

user_email = session['user_email']

strategy_files = os.listdir('data/saved_strategies')

strategies = []

for filename in strategy_files:

if not filename.endswith('.json'):

continue

filepath = os.path.join('data/saved_strategies', filename)

try:

with open(filepath, 'r') as f:

strategy_data = json.load(f)

if 'user_email' not in strategy_data or strategy_data['user_email'] == user_email:

strategies.append(filename.replace('.json', ''))

except:

continue

return jsonify({"strategies": strategies})

except Exception as e:

return jsonify({'error': str(e)}), 500

@app.route('/api/load-strategy', methods=['GET'])
def load_strategy():

if 'user_email' not in session:

return jsonify({"error": "Please log in to load a strategy"}), 401

try:

strategy_name = request.args.get('name')

user_email = session['user_email']

if not strategy_name:

# Return a list of strategies instead

return list_strategies()

filepath = os.path.join('data/saved_strategies', f"{strategy_name}.json")

if not os.path.exists(filepath):

return jsonify({"error": f"Strategy '{strategy_name}' not found"}), 404

try:

with open(filepath, 'r') as f:

strategy_data = json.load(f)

except json.JSONDecodeError:

return jsonify({"error": "Invalid strategy file format"}), 400

except PermissionError:

return jsonify({"error": "Permission denied accessing strategy file"}), 403

if 'user_email' in strategy_data and strategy_data['user_email'] != user_email:

return jsonify({"error": "You don't have permission to access this strategy"}), 403

return jsonify(strategy_data)

except Exception as e:
return jsonify({'error': str(e)}), 500

if __name__ == '__main__':

app.run(debug=True)

APCA_API_KEY_ID="PK2N1Y7GA"

APCA_API_SECRET_KEY="MnXGX930YGC8l0fje8NYPHAS"

SECRET_KEY="85b75e514785f10ad6a8b83eacb8ea6694a113d3aa1bed7ee60953"

SMTP_SERVER=smtp.gmail.com

SMTP_PORT=587

[email protected]

SMTP_PASSWORD="ozdr bkyq"

# Initialize utils package

from utils.backtest_engine import BacktestEngine

from utils.data_fetcher import DataFetcher

from utils.strategy_parser import StrategyParser

__all__ = ['BacktestEngine', 'DataFetcher', 'StrategyParser']

import smtplib

import os

import random

import string

import json

from email.mime.text import MIMEText

from email.mime.multipart import MIMEMultipart

from flask import session


# Store verification codes temporarily

VERIFICATION_CODES = {}

USER_DATA_FILE = 'data/users.json'

# Ensure users file exists

os.makedirs(os.path.dirname(USER_DATA_FILE), exist_ok=True)

if not os.path.exists(USER_DATA_FILE):

with open(USER_DATA_FILE, 'w') as f:

json.dump({}, f)

def load_users():

"""Load users from JSON file"""

try:

with open(USER_DATA_FILE, 'r') as f:

return json.load(f)

except:

return {}

def save_users(users):

"""Save users to JSON file"""

with open(USER_DATA_FILE, 'w') as f:

json.dump(users, f, indent=4)

def generate_verification_code():

"""Generate a 6-digit verification code"""

return ''.join(random.choices(string.digits, k=6))

def send_verification_email(email, code):


"""Send verification email with code"""

# Get email credentials from environment

smtp_server = os.getenv('SMTP_SERVER', 'smtp.gmail.com')

smtp_port = int(os.getenv('SMTP_PORT', 587))

smtp_user = os.getenv('SMTP_USER', '')

smtp_password = os.getenv('SMTP_PASSWORD', '')

if not smtp_user or not smtp_password:

# Use a simulated email for development

print(f"DEVELOPMENT MODE: Verification code for {email} is {code}")

return True

# Create email message

message = MIMEMultipart()

message['From'] = smtp_user

message['To'] = email

message['Subject'] = 'AlgoBlocks Verification Code'

# Email body

body = f"""

<html>

<body>

<h2>AlgoBlocks Verification Code</h2>

<p>Your verification code is: <strong>{code}</strong></p>

<p>This code will expire in 10 minutes.</p>

</body>

</html>

"""
message.attach(MIMEText(body, 'html'))

try:

# Connect to SMTP server

server = smtplib.SMTP(smtp_server, smtp_port)

server.starttls()

server.login(smtp_user, smtp_password)

server.send_message(message)

server.quit()

return True

except Exception as e:

print(f"Error sending email: {e}")

return False

def register_user(email, password, username):

"""Register a new user"""

users = load_users()

if email in users:

return False, "Email already registered"

# Generate verification code

code = generate_verification_code()

VERIFICATION_CODES[email] = code

# Send verification email

if send_verification_email(email, code):

# Store user details temporarily

users[email] = {
'username': username,

'password': password, # In production, use password hashing!

'verified': False,

'portfolio': {

'cash': 10000.00,

'trades': []

save_users(users)

return True, "Verification email sent"

else:

return False, "Failed to send verification email"

def verify_user(email, code):

"""Verify a user's email"""

if email not in VERIFICATION_CODES or VERIFICATION_CODES[email] != code:

return False, "Invalid verification code"

users = load_users()

if email in users:

users[email]['verified'] = True

save_users(users)

# Clean up verification code

del VERIFICATION_CODES[email]

return True, "Email verified successfully"

else:
return False, "User not found"

def login_user(email, password):

"""Log in a user"""

users = load_users()

if email not in users:

return False, "Email not registered"

user = users[email]

if not user['verified']:

return False, "Email not verified"

if user['password'] != password: # In production, use password verification!

return False, "Incorrect password"

# Set session data

session['user_email'] = email

session['username'] = user['username']

return True, "Login successful"

def logout_user():

"""Log out a user"""

session.pop('user_email', None)

session.pop('username', None)

return True, "Logout successful"

import pandas as pd
import numpy as np

import logging

class BacktestEngine:

"""Engine for backtesting trading strategies with Alpaca data"""

def __init__(self, data_fetcher):

"""

Initialize the backtest engine

Args:

data_fetcher: Instance of DataFetcher to get market data

"""

self.data_fetcher = data_fetcher

self.logger = logging.getLogger(__name__)

def run_backtest(self, strategy, symbol='AAPL', start_date=None, end_date=None,


initial_capital=10000.0):

"""

Run a backtest for the given strategy

Args:

strategy (dict): Strategy configuration with indicators and rules

symbol (str): Trading symbol

start_date (str): Start date for backtest (YYYY-MM-DD)

end_date (str): End date for backtest (YYYY-MM-DD)

initial_capital (float): Initial capital amount

Returns:
dict: Backtest results including metrics and trades

"""

# Get historical data

historical_data = self.data_fetcher.get_historical_data(

symbol=symbol,

timeframe='1D',

period='2Y' if not start_date else None

if not historical_data or len(historical_data) < 30:

self.logger.warning(f"Insufficient historical data for {symbol}")

return {

'error': f"Insufficient historical data for {symbol}",

'initial_capital': initial_capital,

'final_equity': initial_capital,

'total_return': 0.0,

'sharpe_ratio': 0.0,

'max_drawdown': 0.0,

'total_trades': 0,

'trades': [],

'equity_curve': [initial_capital]

# Convert to DataFrame

df = pd.DataFrame(historical_data)

df['time'] = pd.to_datetime(df['time'])

df.set_index('time', inplace=True)
# Filter by date range if provided

if start_date:

df = df[df.index >= start_date]

if end_date:

df = df[df.index <= end_date]

# Apply indicators based on strategy

df = self._apply_indicators(df, strategy['indicators'])

# Initialize position tracking columns

df['signal'] = 0 # 1 for buy, -1 for sell, 0 for hold

df['position'] = 0

df['cash'] = initial_capital

df['shares'] = 0

df['equity'] = initial_capital

# Generate signals based on entry/exit rules

for i in range(20, len(df)):

yesterday = df.iloc[i-1]

# Only generate new signals when not in a position

if yesterday['position'] == 0:

entry_signal = self._evaluate_conditions(yesterday, strategy['entry_rules'])

if entry_signal:

df.iloc[i, df.columns.get_loc('signal')] = 1

# Generate exit signals when in a position

elif yesterday['position'] > 0:

exit_signal = self._evaluate_conditions(yesterday, strategy['exit_rules'])


if exit_signal:

df.iloc[i, df.columns.get_loc('signal')] = -1

# Calculate positions - more efficient with vectorized operations

df['position'] = df['signal'].cumsum().clip(lower=0, upper=1)

# Calculate trading results - fill NaN values

df = df.fillna(method='ffill')

# Calculate shares based on signals

for i in range(1, len(df)):

if df['signal'].iloc[i] == 1: # Buy signal

shares_to_buy = int(df['cash'].iloc[i-1] / df['close'].iloc[i])

df.loc[df.index[i], 'shares'] = shares_to_buy

df.loc[df.index[i], 'cash'] = df['cash'].iloc[i-1] - (shares_to_buy * df['close'].iloc[i])

elif df['signal'].iloc[i] == -1: # Sell signal

df.loc[df.index[i], 'cash'] = df['cash'].iloc[i-1] + (df['shares'].iloc[i-1] *


df['close'].iloc[i])

df.loc[df.index[i], 'shares'] = 0

else: # No action

df.loc[df.index[i], 'shares'] = df['shares'].iloc[i-1]

df.loc[df.index[i], 'cash'] = df['cash'].iloc[i-1]

# Calculate equity

df['equity'] = df['cash'] + (df['shares'] * df['close'])

# Generate trade list

trades = []
for i in range(1, len(df)):

if df['signal'].iloc[i] == 1: # Buy signal

trades.append({

'date': df.index[i].strftime('%Y-%m-%d'),

'type': 'BUY',

'price': df['close'].iloc[i],

'shares': df['shares'].iloc[i],

'value': df['shares'].iloc[i] * df['close'].iloc[i]

})

elif df['signal'].iloc[i] == -1: # Sell signal

trades.append({

'date': df.index[i].strftime('%Y-%m-%d'),

'type': 'SELL',

'price': df['close'].iloc[i],

'shares': df['shares'].iloc[i-1],

'value': df['shares'].iloc[i-1] * df['close'].iloc[i]

})

# Calculate performance metrics

equity_curve = df['equity'].tolist()

# Return results

return {

'initial_capital': initial_capital,

'final_equity': round(df['equity'].iloc[-1], 2),

'total_return': round(((df['equity'].iloc[-1] / initial_capital) - 1) * 100, 2),

'sharpe_ratio': round(self._calculate_sharpe_ratio(df['equity']), 2),

'max_drawdown': round(self._calculate_max_drawdown(equity_curve), 2),


'total_trades': len(trades),

'trades': trades,

'equity_curve': [round(eq, 2) for eq in equity_curve]

def _apply_indicators(self, df, indicators):

"""

Apply technical indicators to the DataFrame

Args:

df (DataFrame): Price data

indicators (list): List of indicator configurations

Returns:

DataFrame: DataFrame with indicators added

"""

for indicator in indicators:

try:

if indicator['type'] == 'SMA':

period = indicator['parameters']['period']

df[f'SMA_{period}'] = df['close'].rolling(window=period).mean()

elif indicator['type'] == 'EMA':

period = indicator['parameters']['period']

df[f'EMA_{period}'] = df['close'].ewm(span=period, adjust=False).mean()

elif indicator['type'] == 'RSI':

period = indicator['parameters']['period']
delta = df['close'].diff()

gain = delta.where(delta > 0, 0).rolling(window=period).mean()

loss = -delta.where(delta < 0, 0).rolling(window=period).mean()

# Avoid division by zero

loss = loss.replace(0, np.nan)

rs = gain / loss

rs = rs.fillna(0)

df[f'RSI_{period}'] = 100 - (100 / (1 + rs))

elif indicator['type'] == 'MACD':

fast_period = indicator['parameters']['fast_period']

slow_period = indicator['parameters']['slow_period']

signal_period = indicator['parameters']['signal_period']

# Calculate MACD components

df[f'EMA_{fast_period}'] = df['close'].ewm(span=fast_period,
adjust=False).mean()

df[f'EMA_{slow_period}'] = df['close'].ewm(span=slow_period,
adjust=False).mean()

df['MACD'] = df[f'EMA_{fast_period}'] - df[f'EMA_{slow_period}']

df['MACD_Signal'] = df['MACD'].ewm(span=signal_period, adjust=False).mean()

df['MACD_Hist'] = df['MACD'] - df['MACD_Signal']

except Exception as e:

self.logger.error(f"Error calculating indicator {indicator['type']}: {str(e)}")

return df
def _evaluate_conditions(self, row, conditions):

"""

Evaluate if trading conditions are met

Args:

row (Series): DataFrame row with indicator values

conditions (list): List of condition configurations

Returns:

bool: True if conditions are met, False otherwise

"""

if not conditions:

return False

results = []

for condition in conditions:

indicator = condition['indicator']

operator = condition['operator']

value = condition['value']

# If indicator isn't in the row, skip this condition

if indicator not in row:

self.logger.warning(f"Indicator '{indicator}' not found in data")

continue

# Get the indicator value

indicator_value = row[indicator]
# Handle None or NaN values

if pd.isna(indicator_value):

continue

# Process the value to compare against

compare_value = None

# If value is another column name, get its value

if isinstance(value, str) and value in row:

compare_value = row[value]

else:

# Handle special values

if value == 'close':

compare_value = row['close']

elif value == 'open':

compare_value = row['open']

elif value == 'high':

compare_value = row['high']

elif value == 'low':

compare_value = row['low']

else:

# Try to convert to float

try:

compare_value = float(value)

except (ValueError, TypeError):

continue
# Handle None or NaN values in the comparison value

if pd.isna(compare_value):

continue

# Apply operator

try:

if operator == '>':

results.append(indicator_value > compare_value)

elif operator == '<':

results.append(indicator_value < compare_value)

elif operator == '==':

results.append(indicator_value == compare_value)

elif operator == '>=':

results.append(indicator_value >= compare_value)

elif operator == '<=':

results.append(indicator_value <= compare_value)

except Exception as e:

self.logger.error(f"Error evaluating condition: {str(e)}")

continue

# If no conditions could be evaluated, return False

if not results:

return False

# Return True if all conditions are met

return all(results)

def _calculate_max_drawdown(self, equity_curve):


"""

Calculate maximum drawdown percentage

Args:

equity_curve (list): List of equity values over time

Returns:

float: Maximum drawdown percentage

"""

equity = np.array(equity_curve)

# Calculate the running maximum

running_max = np.maximum.accumulate(equity)

# Calculate the drawdown

drawdowns = (running_max - equity) / running_max

# Handle any NaN values

drawdowns = np.nan_to_num(drawdowns)

# Calculate the maximum drawdown as a percentage

max_drawdown = np.max(drawdowns) * 100

return max_drawdown

def _calculate_sharpe_ratio(self, equity_series, risk_free_rate=0.0):

"""

Calculate Sharpe ratio


Args:

equity_series (Series): Series of equity values

risk_free_rate (float): Risk-free rate (default: 0.0)

Returns:

float: Sharpe ratio

"""

# Calculate daily returns

returns = equity_series.pct_change().dropna()

# Avoid division by zero

if len(returns) < 2 or returns.std() == 0:

return 0.0

# Calculate Sharpe ratio (annualized)

sharpe = (returns.mean() - risk_free_rate) / returns.std() * np.sqrt(252)

return sharpe

import pandas as pd

import numpy as np

from datetime import datetime, timedelta

import logging

class DataFetcher:

"""Class for fetching market data from Alpaca"""


def __init__(self, api):

"""Initialize with an Alpaca API client"""

self.api = api

self.cache = {} # Simple cache for historical data

self.logger = logging.getLogger(__name__)

def get_historical_data(self, symbol, timeframe='1D', period='1Y'):

"""

Get historical price data for a symbol

Args:

symbol (str): Trading symbol (e.g., 'AAPL')

timeframe (str): Timeframe for the data ('1D', '1H', '15Min', '5Min', '1Min')

period (str): Time period to fetch ('1D', '1W', '1M', '3M', '6M', '1Y', '5Y')

Returns:

list: A list of dictionaries containing historical price data

"""

# Generate cache key

cache_key = f"{symbol}_{timeframe}_{period}"

# Return cached data if available and not expired

if cache_key in self.cache:

cache_time, data = self.cache[cache_key]

# Cache for 1 hour

if datetime.now() - cache_time < timedelta(hours=1):

self.logger.info(f"Using cached data for {cache_key}")

return data
# Calculate start and end dates based on period

end_date = datetime.now()

if period == '1D':

start_date = end_date - timedelta(days=1)

elif period == '1W':

start_date = end_date - timedelta(weeks=1)

elif period == '1M':

start_date = end_date - timedelta(days=30)

elif period == '3M':

start_date = end_date - timedelta(days=90)

elif period == '6M':

start_date = end_date - timedelta(days=180)

elif period == '1Y':

start_date = end_date - timedelta(days=365)

elif period == '2Y':

start_date = end_date - timedelta(days=365*2)

else: # '5Y'

start_date = end_date - timedelta(days=365 * 5)

# Format dates for API call

start_str = start_date.strftime('%Y-%m-%d')

end_str = end_date.strftime('%Y-%m-%d')

# Map timeframe to Alpaca format

timeframe_map = {

'1Min': '1Min',

'5Min': '5Min',
'15Min': '15Min',

'1H': '1Hour',

'1D': '1Day'

alpaca_timeframe = timeframe_map.get(timeframe, '1Day')

self.logger.info(f"Fetching {symbol} data from {start_str} to {end_str} with timeframe


{alpaca_timeframe}")

# Get data from Alpaca

try:

# Try using the newer API first

try:

bars = self.api.get_bars(

symbol=symbol,

timeframe=alpaca_timeframe,

start=start_str,

end=end_str,

limit=1000

data = []

for bar in bars:

data.append({

'time': bar.t.strftime('%Y-%m-%d %H:%M:%S'),

'open': bar.o,

'high': bar.h,

'low': bar.l,
'close': bar.c,

'volume': bar.v

})

# Cache the results

self.cache[cache_key] = (datetime.now(), data)

return data

except AttributeError:

# Fall back to older barset API

bars = self.api.get_barset(

symbols=symbol,

timeframe=timeframe,

start=start_str,

end=end_str,

limit=1000

# Convert to list of dictionaries

if symbol in bars:

data = []

for bar in bars[symbol]:

data.append({

'time': bar.t.strftime('%Y-%m-%d %H:%M:%S'),

'open': bar.o,

'high': bar.h,

'low': bar.l,

'close': bar.c,
'volume': bar.v

})

# Cache the results

self.cache[cache_key] = (datetime.now(), data)

return data

except Exception as e:

self.logger.error(f"Error fetching data for {symbol}: {str(e)}")

# Fall back to sample data if the API call fails

self.logger.warning(f"Falling back to sample data for {symbol}")

sample_data = self._get_sample_data(symbol)

# Cache the sample data too

self.cache[cache_key] = (datetime.now(), sample_data)

return sample_data

def _get_sample_data(self, symbol):

"""Generate sample data for testing when API is unavailable"""

# Create a date range for the past year

end_date = datetime.now()

start_date = end_date - timedelta(days=365)

dates = pd.date_range(start=start_date, end=end_date, freq='B') # Business days

# Set a random seed based on the symbol for consistent results

seed = sum(ord(c) for c in symbol)


np.random.seed(seed)

# Generate sample data with realistic price action

base_price = 100.0 + (seed % 400) # Different base price per symbol

volatility = 0.01 + (seed % 100) * 0.0001 # Different volatility per symbol

data = []

current_price = base_price

# Generate an uptrend, downtrend, or sideways pattern

trend = np.random.choice(['up', 'down', 'sideways'])

if trend == 'up':

drift = 0.0005

elif trend == 'down':

drift = -0.0005

else:

drift = 0.0

for date in dates:

# Random daily volatility with drift

price_change = np.random.normal(drift, volatility) * current_price

current_price = max(current_price + price_change, 1.0) # Ensure price doesn't go


below 1

# Daily high and low with realistic relationship to open/close

if np.random.random() > 0.5: # Bullish day

open_price = current_price * (1 - np.random.random() * volatility)

close_price = current_price
high_price = close_price * (1 + np.random.random() * volatility)

low_price = open_price * (1 - np.random.random() * volatility)

else: # Bearish day

open_price = current_price * (1 + np.random.random() * volatility)

close_price = current_price

high_price = open_price * (1 + np.random.random() * volatility)

low_price = close_price * (1 - np.random.random() * volatility)

# Random volume

volume = int(np.random.randint(100000, 10000000))

data.append({

'time': date.strftime('%Y-%m-%d'),

'open': round(open_price, 2),

'high': round(high_price, 2),

'low': round(low_price, 2),

'close': round(close_price, 2),

'volume': volume

})

return data

class StrategyParser:

"""Utility class to parse strategy blocks into strategy configuration"""

def __init__(self, blocks):

"""

Initialize the parser with a list of blocks


Args:

blocks (list): List of block configurations

"""

self.blocks = blocks or []

def parse_blocks(self):

"""

Parse blocks into a strategy configuration

Returns:

dict: Strategy configuration

"""

indicators = []

entry_rules = []

exit_rules = []

# Process indicator blocks

for block in self.blocks:

if block.get('type') == 'indicator':

indicator_type = block.get('indicatorType')

if indicator_type == 'SMA':

indicators.append({

'type': 'SMA',

'parameters': {

'period': block.get('period', 20)

}
})

elif indicator_type == 'EMA':

indicators.append({

'type': 'EMA',

'parameters': {

'period': block.get('period', 20)

})

elif indicator_type == 'RSI':

indicators.append({

'type': 'RSI',

'parameters': {

'period': block.get('period', 14)

})

elif indicator_type == 'MACD':

indicators.append({

'type': 'MACD',

'parameters': {

'fast_period': block.get('fastPeriod', 12),

'slow_period': block.get('slowPeriod', 26),

'signal_period': block.get('signalPeriod', 9)

})

# Process rule blocks

elif block.get('type') == 'entry':

for condition in block.get('conditions', []):


entry_rules.append(condition)

elif block.get('type') == 'exit':

for condition in block.get('conditions', []):

exit_rules.append(condition)

return {

'indicators': indicators,

'entry_rules': entry_rules,

'exit_rules': exit_rules

import os

import json

import logging

from datetime import datetime

from flask import session

logger = logging.getLogger(__name__)

USER_DATA_FILE = 'data/users.json'

def load_users():

"""Load users from JSON file"""

try:

with open(USER_DATA_FILE, 'r') as f:

return json.load(f)

except Exception as e:

logger.error(f"Error loading users: {str(e)}")


return {}

def save_users(users):

"""Save users to JSON file"""

try:

with open(USER_DATA_FILE, 'w') as f:

json.dump(users, f, indent=4)

return True

except Exception as e:

logger.error(f"Error saving users: {str(e)}")

return False

def save_paper_trade(email, trade_data):

"""

Save a paper trade for a user

Args:

email (str): User email

trade_data (dict): Trade data

Returns:

tuple: (success, result)

"""

try:

users = load_users()

if email not in users:

return False, "User not found"


# Extract trade details

symbol = trade_data.get('symbol')

quantity = int(trade_data.get('quantity', 0))

side = trade_data.get('side', 'buy').lower()

order_type = trade_data.get('orderType', 'market').lower()

price = float(trade_data.get('price', 0)) if order_type == 'limit' else None

notes = trade_data.get('notes', '')

# Validate trade data

if not symbol:

return False, "Symbol is required"

if quantity <= 0:

return False, "Quantity must be positive"

if side not in ['buy', 'sell']:

return False, "Side must be 'buy' or 'sell'"

if order_type not in ['market', 'limit']:

return False, "Order type must be 'market' or 'limit'"

if order_type == 'limit' and (price is None or price <= 0):

return False, "Valid price is required for limit orders"

# Get user portfolio

portfolio = users[email]['portfolio']

cash = portfolio.get('cash', 10000.0)


# Process the trade

timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# Create trade record

trade = {

'timestamp': timestamp,

'symbol': symbol,

'quantity': quantity,

'side': side,

'type': order_type,

'price': price if price else 100.0, # Mock price for demo

'status': 'executed',

'notes': notes

# Update portfolio cash (simplified)

if side == 'buy':

trade_value = quantity * trade['price']

if cash < trade_value:

return False, "Insufficient funds"

portfolio['cash'] = cash - trade_value

else: # sell

trade_value = quantity * trade['price']

portfolio['cash'] = cash + trade_value

# Add trade to history

if 'trades' not in portfolio:


portfolio['trades'] = []

portfolio['trades'].append(trade)

# Save updated user data

success = save_users(users)

if not success:

return False, "Failed to save trade"

return True, trade

except Exception as e:

logger.error(f"Error saving paper trade: {str(e)}")

return False, str(e)

def get_user_portfolio(email):

"""

Get a user's portfolio data

Args:

email (str): User email

Returns:

dict: Portfolio data

"""

try:

users = load_users()
if email not in users:

return None

portfolio = users[email].get('portfolio', {})

# Calculate portfolio statistics

trades = portfolio.get('trades', [])

# Base stats

stats = {

'total_trades': len(trades),

'win_rate': 0.0,

'profit_loss': 0.0,

'active_positions': []

# Calculate profit/loss and track positions

position_tracker = {}

profitable_trades = 0

for trade in trades:

symbol = trade['symbol']

side = trade['side']

quantity = trade['quantity']

price = trade['price']

# Track positions

if side == 'buy':
if symbol not in position_tracker:

position_tracker[symbol] = {

'quantity': 0,

'avg_price': 0,

'total_cost': 0

current = position_tracker[symbol]

new_quantity = current['quantity'] + quantity

total_cost = current['total_cost'] + (quantity * price)

position_tracker[symbol] = {

'quantity': new_quantity,

'avg_price': total_cost / new_quantity if new_quantity > 0 else 0,

'total_cost': total_cost

elif side == 'sell':

if symbol in position_tracker:

current = position_tracker[symbol]

# Calculate P/L for this sale

if current['quantity'] > 0:

sell_value = quantity * price

avg_cost = quantity * current['avg_price']

trade_pl = sell_value - avg_cost

stats['profit_loss'] += trade_pl
if trade_pl > 0:

profitable_trades += 1

# Update position

new_quantity = current['quantity'] - quantity

if new_quantity <= 0:

position_tracker[symbol] = {

'quantity': 0,

'avg_price': 0,

'total_cost': 0

else:

# Proportionally reduce total cost

remaining_ratio = new_quantity / current['quantity']

position_tracker[symbol] = {

'quantity': new_quantity,

'avg_price': current['avg_price'],

'total_cost': current['total_cost'] * remaining_ratio

# Calculate win rate

if len(trades) > 0:

stats['win_rate'] = (profitable_trades / len(trades)) * 100

# Format active positions

for symbol, position in position_tracker.items():

if position['quantity'] > 0:

stats['active_positions'].append({
'symbol': symbol,

'quantity': position['quantity'],

'avg_price': position['avg_price'],

'total_cost': position['total_cost']

})

return {

'cash': portfolio.get('cash', 10000.0),

'trades': trades,

'stats': stats

except Exception as e:

logger.error(f"Error getting user portfolio: {str(e)}")

return None

"title": "Welcome to ALGOblocks!",

"body": "This tutorial will guide you through building and backtesting trading strategies."

},

"title": "Step 1: Select a Symbol",

"body": "Use the symbol search to select a stock you want to trade."

},

"title": "Step 2: Drag and Drop Blocks",

"body": "Drag indicator and rule blocks onto the canvas to build your strategy."
},

"title": "Step 3: Configure Blocks",

"body": "Click on each block to configure its parameters (e.g., period for SMA)."

},

"title": "Step 4: Run Backtest",

"body": "Click 'Run Backtest' to see how your strategy would have performed."

},

"title": "Step 5: Paper Trade",

"body": "Use 'Paper Trade' to simulate real trades and track your portfolio."

"term": "Stock",

"definition": "A share in the ownership of a company."

},

"term": "Indicator",

"definition": "A mathematical calculation based on price, volume, or open interest, used
to predict market direction."

},

"term": "Backtest",

"definition": "Testing a trading strategy using historical data."


},

"term": "Paper Trading",

"definition": "Simulated trading with no real money at risk."

},

"term": "Equity Curve",

"definition": "A graph showing the value of a trading account over time."

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>AlgoBlocks - Algorithmic Trading Platform</title>

<!-- Bootstrap CSS -->

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">

<!-- Bootstrap Icons -->

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-


[email protected]/font/bootstrap-icons.css">

<!-- TradingView Widget -->

<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>

<!-- Custom CSS -->

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

</head>

<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">

<div class="container-fluid">

<a class="navbar-brand" href="{{ url_for('index') }}">

<i class="bi bi-bar-chart-fill me-2"></i>AlgoBlocks

</a>

<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-


target="#navbarNav">

<span class="navbar-toggler-icon"></span>

</button>

<div class="collapse navbar-collapse" id="navbarNav">

<ul class="navbar-nav">

<li class="nav-item">

<a class="nav-link active" href="{{ url_for('index') }}">Strategy Builder</a>

</li>

<li class="nav-item">

<a class="nav-link" href="{{ url_for('portfolio') }}">Portfolio</a>

</li>

<li class="nav-item">

<a class="nav-link" href="{{ url_for('tutorial') }}">Tutorial</a>

</li>

</ul>

<ul class="navbar-nav ms-auto">

<li class="nav-item">

<span class="nav-link" id="marketStatus">Market Status: Loading...</span>

</li>

<li class="nav-item dropdown">

<a class="nav-link dropdown-toggle" href="#" id="userDropdown"


role="button" data-bs-toggle="dropdown">

<i class="bi bi-person-circle me-1"></i>{{ session.get('username', 'User') }}


</a>

<ul class="dropdown-menu dropdown-menu-end">

<li><a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a></li>

</ul>

</li>

</ul>

</div>

</div>

</nav>

<div class="container-fluid">

<div class="row">

<!-- Left Sidebar -->

<div class="col-md-3 col-lg-2 d-md-block bg-light sidebar">

<div class="position-sticky pt-3">

<h5 class="sidebar-heading d-flex justify-content-between align-items-center px-


3 mt-4 mb-1 text-muted">

<span>Strategy Blocks</span>

</h5>

<div class="block-container px-3">

<!-- Indicators -->

<div class="block-group">

<h6 class="fw-bold">Indicators</h6>

<div class="block indicator-block" draggable="true" data-type="indicator"


data-indicator-type="SMA">

<i class="bi bi-graph-up me-1"></i> Simple Moving Average (SMA)

</div>

<div class="block indicator-block" draggable="true" data-type="indicator"


data-indicator-type="EMA">
<i class="bi bi-graph-up-arrow me-1"></i> Exponential Moving Average
(EMA)

</div>

<div class="block indicator-block" draggable="true" data-type="indicator"


data-indicator-type="RSI">

<i class="bi bi-activity me-1"></i> Relative Strength Index (RSI)

</div>

<div class="block indicator-block" draggable="true" data-type="indicator"


data-indicator-type="MACD">

<i class="bi bi-bar-chart-line me-1"></i> MACD

</div>

</div>

<!-- Rules -->

<div class="block-group">

<h6 class="fw-bold">Rules</h6>

<div class="block rule-block entry-block" draggable="true" data-


type="entry">

<i class="bi bi-arrow-up-circle me-1"></i> Entry Rule

</div>

<div class="block rule-block exit-block" draggable="true" data-type="exit">

<i class="bi bi-arrow-down-circle me-1"></i> Exit Rule

</div>

</div>

</div>

<h5 class="sidebar-heading d-flex justify-content-between align-items-center px-


3 mt-4 mb-1 text-muted">

<span>Backtest Settings</span>

</h5>

<form id="backtestForm" class="px-3">


<div class="mb-3">

<label for="symbolSearch" class="form-label">Symbol</label>

<div class="input-group mb-1">

<input type="text" class="form-control" id="symbolSearch"


placeholder="Search symbols...">

<button class="btn btn-outline-secondary" type="button"


id="searchSymbolBtn">

<i class="bi bi-search"></i>

</button>

</div>

<select class="form-select" id="symbol">

<option value="AAPL">AAPL - Apple Inc</option>

<option value="MSFT">MSFT - Microsoft Corp</option>

<option value="GOOGL">GOOGL - Alphabet Inc</option>

<option value="AMZN">AMZN - Amazon.com Inc</option>

<option value="META">META - Meta Platforms Inc</option>

<option value="TSLA">TSLA - Tesla Inc</option>

<option value="NVDA">NVDA - NVIDIA Corp</option>

<option value="JPM">JPM - JPMorgan Chase & Co</option>

<option value="V">V - Visa Inc</option>

<option value="JNJ">JNJ - Johnson & Johnson</option>

</select>

</div>

<div class="mb-3">

<label for="startDate" class="form-label">Start Date</label>

<input type="date" class="form-control" id="startDate">

</div>

<div class="mb-3">

<label for="endDate" class="form-label">End Date</label>


<input type="date" class="form-control" id="endDate">

</div>

<div class="mb-3">

<label for="initialCapital" class="form-label">Initial Capital</label>

<div class="input-group">

<span class="input-group-text">$</span>

<input type="number" class="form-control" id="initialCapital"


value="10000">

</div>

</div>

<button type="button" class="btn btn-primary w-100 mb-2"


id="runBacktestBtn">

<i class="bi bi-play-fill"></i> Run Backtest

</button>

<button type="button" class="btn btn-success w-100" id="paperTradeBtn">

<i class="bi bi-cash-coin"></i> Paper Trade

</button>

</form>

</div>

</div>

<!-- Main Content Area -->

<div class="col-md-9 ms-sm-auto col-lg-10 px-md-4">

<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-


center pt-3 pb-2 mb-3 border-bottom">

<h1 class="h2">Strategy Builder</h1>

<div class="btn-toolbar mb-2 mb-md-0">

<div class="btn-group me-2">

<button type="button" class="btn btn-sm btn-outline-secondary"


id="saveStrategyBtn">
<i class="bi bi-save"></i> Save

</button>

<button type="button" class="btn btn-sm btn-outline-secondary"


id="loadStrategyBtn">

<i class="bi bi-folder2-open"></i> Load

</button>

<button type="button" class="btn btn-sm btn-outline-secondary"


id="clearStrategyBtn">

<i class="bi bi-trash"></i> Clear

</button>

</div>

</div>

</div>

<!-- Strategy Canvas -->

<div class="row mb-4">

<div class="col-12">

<div id="strategyCanvas" class="strategy-canvas">

<div class="canvas-placeholder">

<i class="bi bi-plus-circle-dotted fs-1"></i>

<p>Drag and drop blocks here to build your strategy</p>

</div>

</div>

</div>

</div>

<!-- Chart Container -->

<div class="row mb-4">

<div class="col-12">

<div class="card">

<div class="card-header">
<h5 class="card-title mb-0">Price Chart</h5>

</div>

<div class="card-body p-0">

<div id="tradingViewChart" style="height: 400px;"></div>

</div>

</div>

</div>

</div>

<!-- Results & Metrics -->

<div class="row">

<!-- Performance Metrics -->

<div class="col-md-4 mb-4">

<div class="card">

<div class="card-header">

<h5 class="card-title mb-0">Performance Metrics</h5>

</div>

<div class="card-body">

<div id="performanceMetrics">

<p class="text-muted text-center">

<i class="bi bi-arrow-clockwise"></i> Run a backtest to see metrics

</p>

</div>

</div>

</div>

</div>

<!-- Equity Curve -->

<div class="col-md-8 mb-4">

<div class="card">
<div class="card-header">

<h5 class="card-title mb-0">Equity Curve</h5>

</div>

<div class="card-body">

<canvas id="equityCurveChart" height="250"></canvas>

</div>

</div>

</div>

</div>

<!-- Trades Table -->

<div class="row mb-4">

<div class="col-12">

<div class="card">

<div class="card-header">

<h5 class="card-title mb-0">Trades</h5>

</div>

<div class="card-body">

<div class="table-responsive">

<table class="table table-striped table-sm">

<thead>

<tr>

<th>Date</th>

<th>Type</th>

<th>Price</th>

<th>Shares</th>

<th>Value</th>

</tr>

</thead>
<tbody id="tradesTableBody">

<!-- Trades will be inserted here -->

<tr>

<td colspan="5" class="text-center text-muted">No trades to


display</td>

</tr>

</tbody>

</table>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

<!-- Block Configuration Modal -->

<div class="modal fade" id="blockConfigModal" tabindex="-1" aria-hidden="true">

<div class="modal-dialog">

<div class="modal-content">

<div class="modal-header">

<h5 class="modal-title">Configure Block</h5>

<button type="button" class="btn-close" data-bs-dismiss="modal" aria-


label="Close"></button>

</div>

<div class="modal-body" id="blockConfigBody">

<!-- Modal content will be dynamically inserted here -->

</div>

<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">Cancel</button>

<button type="button" class="btn btn-primary"


id="saveBlockConfigBtn">Save</button>

</div>

</div>

</div>

</div>

<!-- Strategy Load Modal -->

<div class="modal fade" id="loadStrategyModal" tabindex="-1" aria-hidden="true">

<div class="modal-dialog">

<div class="modal-content">

<div class="modal-header">

<h5 class="modal-title">Load Strategy</h5>

<button type="button" class="btn-close" data-bs-dismiss="modal"></button>

</div>

<div class="modal-body">

<div id="strategyList" class="list-group">

<!-- Strategy list will be inserted here -->

<div class="text-center py-3">

<div class="spinner-border text-primary" role="status"></div>

<p class="mt-2">Loading strategies...</p>

</div>

</div>

</div>

<div class="modal-footer">

<button type="button" class="btn btn-secondary" data-bs-


dismiss="modal">Cancel</button>

</div>
</div>

</div>

</div>

<!-- Paper Trade Modal -->

<div class="modal fade" id="paperTradeModal" tabindex="-1" aria-hidden="true">

<div class="modal-dialog">

<div class="modal-content">

<div class="modal-header">

<h5 class="modal-title">Paper Trade</h5>

<button type="button" class="btn-close" data-bs-dismiss="modal" aria-


label="Close"></button>

</div>

<div class="modal-body">

<form id="paperTradeForm">

<div class="mb-3">

<label for="tradeSymbol" class="form-label">Symbol</label>

<input type="text" class="form-control" id="tradeSymbol" readonly>

</div>

<div class="mb-3">

<label for="tradeQuantity" class="form-label">Quantity</label>

<input type="number" class="form-control" id="tradeQuantity" min="1"


value="1">

</div>

<div class="mb-3">

<label for="tradeSide" class="form-label">Side</label>

<select class="form-select" id="tradeSide">

<option value="buy">Buy</option>

<option value="sell">Sell</option>

</select>
</div>

<div class="mb-3">

<label for="tradeType" class="form-label">Order Type</label>

<select class="form-select" id="tradeType">

<option value="market">Market</option>

<option value="limit">Limit</option>

</select>

</div>

<div class="mb-3 d-none" id="limitPriceGroup">

<label for="limitPrice" class="form-label">Limit Price</label>

<div class="input-group">

<span class="input-group-text">$</span>

<input type="number" class="form-control" id="limitPrice" step="0.01">

</div>

</div>

<div class="mb-3">

<label for="tradeNotes" class="form-label">Notes</label>

<textarea class="form-control" id="tradeNotes" rows="2"></textarea>

</div>

</form>

</div>

<div class="modal-footer">

<button type="button" class="btn btn-secondary" data-bs-


dismiss="modal">Cancel</button>

<button type="button" class="btn btn-success" id="submitTradeBtn">Submit


Order</button>

</div>

</div>

</div>
</div>

<!-- Bootstrap JS -->

<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></scrip
t>

<!-- Chart.js -->

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<!-- Custom JavaScript -->

<script src="{{ url_for('static', filename='js/script.js') }}"></script>

</body>

</html>

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Login - AlgoBlocks</title>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-


[email protected]/font/bootstrap-icons.css">

<link rel="stylesheet" href="{{ url_for('static', filename='css/auth.css') }}">

</head>

<body class="text-center">

<main class="form-signin">

<form method="POST">

<i class="bi bi-bar-chart-fill text-primary mb-2" style="font-size: 3rem;"></i>

<h1 class="h3 mb-3 fw-normal">AlgoBlocks Login</h1>


{% with messages = get_flashed_messages(with_categories=true) %}

{% if messages %}

{% for category, message in messages %}

<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">

{{ message }}

<button type="button" class="btn-close" data-bs-dismiss="alert" aria-


label="Close"></button>

</div>

{% endfor %}

{% endif %}

{% endwith %}

<div class="form-floating">

<input type="email" class="form-control" id="email" name="email"


placeholder="[email protected]" required>

<label for="email">Email address</label>

</div>

<div class="form-floating">

<input type="password" class="form-control" id="password" name="password"


placeholder="Password" required>

<label for="password">Password</label>

</div>

<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>

<p class="mt-3 mb-3 text-muted">Don't have an account? <a href="{{


url_for('register') }}">Register</a></p>

</form>

</main>

<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>
</html>

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Portfolio - AlgoBlocks</title>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-


[email protected]/font/bootstrap-icons.css">

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

</head>

<body>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">

<div class="container-fluid">

<a class="navbar-brand" href="{{ url_for('index') }}">

<i class="bi bi-bar-chart-fill me-2"></i>AlgoBlocks

</a>

<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-


target="#navbarNav">

<span class="navbar-toggler-icon"></span>

</button>

<div class="collapse navbar-collapse" id="navbarNav">

<ul class="navbar-nav">

<li class="nav-item">

<a class="nav-link" href="{{ url_for('index') }}">Strategy Builder</a>

</li>
<li class="nav-item">

<a class="nav-link active" href="{{ url_for('portfolio') }}">Portfolio</a>

</li>

<li class="nav-item">

<a class="nav-link" href="{{ url_for('tutorial') }}">Tutorial</a>

</li>

</ul>

<ul class="navbar-nav ms-auto">

<li class="nav-item">

<span class="nav-link" id="marketStatus">Market Status: Loading...</span>

</li>

<li class="nav-item dropdown">

<a class="nav-link dropdown-toggle" href="#" id="userDropdown"


role="button" data-bs-toggle="dropdown">

<i class="bi bi-person-circle me-1"></i>{{ session.get('username', 'User') }}

</a>

<ul class="dropdown-menu dropdown-menu-end">

<li><a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a></li>

</ul>

</li>

</ul>

</div>

</div>

</nav>

<div class="container-fluid py-4">

<div class="row mb-4">

<div class="col-12">

<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">

<h5 class="mb-0">Portfolio Overview</h5>

<button id="refreshPortfolioBtn" class="btn btn-sm btn-outline-primary">

<i class="bi bi-arrow-clockwise me-1"></i> Refresh

</button>

</div>

<div class="card-body">

<div class="row">

<div class="col-md-6">

<div class="card mb-3">

<div class="card-header bg-light">

<h6 class="mb-0">Account Balance</h6>

</div>

<div class="card-body">

<h4 class="mb-0 text-success" id="cashBalance">${{


"%.2f"|format(portfolio.cash) }}</h4>

<small class="text-muted">Available cash</small>

</div>

</div>

</div>

<div class="col-md-6">

<div class="card mb-3">

<div class="card-header bg-light">

<h6 class="mb-0">Portfolio Statistics</h6>

</div>

<div class="card-body">

<div class="metric-row">

<span class="metric-label">Total Trades</span>


<span class="metric-value" id="totalTrades">{{
portfolio.stats.total_trades }}</span>

</div>

<div class="metric-row">

<span class="metric-label">Win Rate</span>

<span class="metric-value" id="winRate">{{


"%.2f"|format(portfolio.stats.win_rate) }}%</span>

</div>

<div class="metric-row">

<span class="metric-label">Profit/Loss</span>

<span class="metric-value {{ 'positive' if portfolio.stats.profit_loss >


0 else 'negative' if portfolio.stats.profit_loss < 0 else '' }}" id="profitLoss">

${{ "%.2f"|format(portfolio.stats.profit_loss) }}

</span>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

<div class="row mb-4">

<div class="col-12">

<div class="card">

<div class="card-header">

<h5 class="mb-0">Active Positions</h5>

</div>
<div class="card-body">

<div class="table-responsive">

<table class="table table-striped table-hover" id="positionsTable">

<thead>

<tr>

<th>Symbol</th>

<th>Quantity</th>

<th>Average Price</th>

<th>Current Value</th>

<th>Profit/Loss</th>

</tr>

</thead>

<tbody id="positionsTableBody">

{% if portfolio.stats.active_positions %}

{% for position in portfolio.stats.active_positions %}

<tr>

<td>{{ position.symbol }}</td>

<td>{{ position.quantity }}</td>

<td>${{ "%.2f"|format(position.avg_price) }}</td>

<td>${{ "%.2f"|format(position.total_cost) }}</td>

<td class="text-success">-</td>

</tr>

{% endfor %}

{% else %}

<tr>

<td colspan="5" class="text-center">No active positions</td>

</tr>

{% endif %}
</tbody>

</table>

</div>

</div>

</div>

</div>

</div>

<div class="row">

<div class="col-12">

<div class="card">

<div class="card-header">

<h5 class="mb-0">Trade History</h5>

</div>

<div class="card-body">

<div class="table-responsive">

<table class="table table-striped table-sm" id="tradesTable">

<thead>

<tr>

<th>Date</th>

<th>Symbol</th>

<th>Side</th>

<th>Quantity</th>

<th>Price</th>

<th>Value</th>

<th>Status</th>

<th>Notes</th>

</tr>

</thead>
<tbody id="tradesTableBody">

{% if portfolio.trades %}

{% for trade in portfolio.trades|reverse %}

<tr>

<td>{{ trade.timestamp }}</td>

<td>{{ trade.symbol }}</td>

<td>

<span class="badge {{ 'bg-success' if trade.side == 'buy' else 'bg-


danger' }}">

{{ trade.side|upper }}

</span>

</td>

<td>{{ trade.quantity }}</td>

<td>${{ "%.2f"|format(trade.price) }}</td>

<td>${{ "%.2f"|format(trade.price * trade.quantity) }}</td>

<td>{{ trade.status }}</td>

<td>{{ trade.notes }}</td>

</tr>

{% endfor %}

{% else %}

<tr>

<td colspan="8" class="text-center">No trades found</td>

</tr>

{% endif %}

</tbody>

</table>

</div>

</div>
</div>

</div>

</div>

</div>

<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></scrip
t>

<script src="{{ url_for('static', filename='js/portfolio.js') }}"></script>

</body>

</html>

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Register - AlgoBlocks</title>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-


[email protected]/font/bootstrap-icons.css">

<link rel="stylesheet" href="{{ url_for('static', filename='css/auth.css') }}">

</head>

<body class="text-center">

<main class="form-signup">

<form method="POST">

<i class="bi bi-bar-chart-fill text-primary mb-2" style="font-size: 3rem;"></i>

<h1 class="h3 mb-3 fw-normal">AlgoBlocks Registration</h1>

{% with messages = get_flashed_messages(with_categories=true) %}


{% if messages %}

{% for category, message in messages %}

<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">

{{ message }}

<button type="button" class="btn-close" data-bs-dismiss="alert" aria-


label="Close"></button>

</div>

{% endfor %}

{% endif %}

{% endwith %}

<div class="form-floating">

<input type="text" class="form-control" id="username" name="username"


placeholder="Username" required>

<label for="username">Username</label>

</div>

<div class="form-floating">

<input type="email" class="form-control" id="email" name="email"


placeholder="[email protected]" required>

<label for="email">Email address</label>

</div>

<div class="form-floating">

<input type="password" class="form-control" id="password" name="password"


placeholder="Password" required>

<label for="password">Password</label>

</div>

<button class="w-100 btn btn-lg btn-primary" type="submit">Register</button>

<p class="mt-3 mb-3 text-muted">Already have an account? <a href="{{


url_for('login') }}">Login</a></p>

</form>
</main>

<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>

</html>

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Tutorial - AlgoBlocks</title>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-


[email protected]/font/bootstrap-icons.css">

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

</head>

<body>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">

<div class="container-fluid">

<a class="navbar-brand" href="{{ url_for('index') }}">

<i class="bi bi-bar-chart-fill me-2"></i>AlgoBlocks

</a>

<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-


target="#navbarNav">

<span class="navbar-toggler-icon"></span>

</button>
<div class="collapse navbar-collapse" id="navbarNav">

<ul class="navbar-nav">

<li class="nav-item">

<a class="nav-link" href="{{ url_for('index') }}">Strategy Builder</a>

</li>

<li class="nav-item">

<a class="nav-link" href="{{ url_for('portfolio') }}">Portfolio</a>

</li>

<li class="nav-item">

<a class="nav-link active" href="{{ url_for('tutorial') }}">Tutorial</a>

</li>

</ul>

<ul class="navbar-nav ms-auto">

<li class="nav-item">

<span class="nav-link" id="marketStatus">Market Status: Loading...</span>

</li>

<li class="nav-item dropdown">

<a class="nav-link dropdown-toggle" href="#" id="userDropdown"


role="button" data-bs-toggle="dropdown">

<i class="bi bi-person-circle me-1"></i>{{ session.get('username', 'User') }}

</a>

<ul class="dropdown-menu dropdown-menu-end">

<li><a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a></li>

</ul>

</li>

</ul>

</div>

</div>
</nav>

<div class="container py-4">

<h2 class="mb-4">AlgoBlocks Tutorial</h2>

<div class="row">

<div class="col-md-7">

<div id="tutorialSteps" class="mb-4">

<div class="text-center py-3">

<div class="spinner-border text-primary" role="status"></div>

<p class="mt-2">Loading tutorial content...</p>

</div>

</div>

</div>

<div class="col-md-5">

<div class="card">

<div class="card-header">

<h5 class="card-title mb-0">Stock Market Terms</h5>

</div>

<div class="card-body" id="termsList">

<div class="text-center py-3">

<div class="spinner-border text-primary" role="status"></div>

<p class="mt-2">Loading terms...</p>

</div>

</div>

</div>

</div>

</div>

</div>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></scrip
t>

<script src="{{ url_for('static', filename='js/tutorial.js') }}"></script>

</body>

</html>

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Verify Email - AlgoBlocks</title>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-


[email protected]/font/bootstrap-icons.css">

<link rel="stylesheet" href="{{ url_for('static', filename='css/auth.css') }}">

</head>

<body class="text-center">

<main class="form-verify">

<form method="POST">

<i class="bi bi-envelope-check text-primary mb-2" style="font-size: 3rem;"></i>

<h1 class="h3 mb-3 fw-normal">Verify Your Email</h1>

{% with messages = get_flashed_messages(with_categories=true) %}

{% if messages %}

{% for category, message in messages %}

<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">

{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-
label="Close"></button>

</div>

{% endfor %}

{% endif %}

{% endwith %}

<p class="mb-3">Please check your email for a verification code.</p>

<div class="form-floating mb-3">

<input type="email" class="form-control" id="email" name="email"


placeholder="[email protected]" required>

<label for="email">Email address</label>

</div>

<div class="form-floating mb-3">

<input type="text" class="form-control" id="code" name="code"


placeholder="Verification Code" required>

<label for="code">Verification Code</label>

</div>

<button class="w-100 btn btn-lg btn-primary" type="submit">Verify</button>

<p class="mt-3 mb-3 text-muted">

<a href="{{ url_for('login') }}">Back to Login</a>

</p>

</form>

</main>

<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>

</html>
// static/js/portfolio.js

document.addEventListener('DOMContentLoaded', function() {

// DOM Elements

const refreshBtn = document.getElementById('refreshPortfolioBtn');

const cashBalance = document.getElementById('cashBalance');

const totalTrades = document.getElementById('totalTrades');

const winRate = document.getElementById('winRate');

const profitLoss = document.getElementById('profitLoss');

const positionsTableBody = document.getElementById('positionsTableBody');

const tradesTableBody = document.getElementById('tradesTableBody');

const marketStatus = document.getElementById('marketStatus');

// Fetch market status

fetchMarketStatus();

// Add refresh button event listener

if (refreshBtn) {

refreshBtn.addEventListener('click', refreshPortfolio);

// Fetch market status

function fetchMarketStatus() {

fetch('/api/markets')

.then(response => response.json())

.then(data => {

if (data.is_open) {
marketStatus.innerHTML = 'Market Status: <span class="text-
success">Open</span>';

} else {

const nextOpen = new Date(data.next_open);

marketStatus.innerHTML = `Market Status: <span class="text-


danger">Closed</span> (Opens ${formatDateTime(nextOpen)})`;

})

.catch(error => {

console.error('Error fetching market status:', error);

marketStatus.textContent = 'Market Status: Unknown';

});

// Refresh portfolio data

function refreshPortfolio() {

// Show loading spinner

refreshBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"


aria-hidden="true"></span> Refreshing...';

refreshBtn.disabled = true;

fetch('/api/portfolio/update')

.then(response => response.json())

.then(data => {

if (data.error) {

console.error("Portfolio error:", data.error);

alert("Error refreshing portfolio: " + data.error);

return;

}
updatePortfolioUI(data.portfolio);

})

.catch(error => {

console.error('Error refreshing portfolio:', error);

alert("Error refreshing portfolio");

})

.finally(() => {

// Reset button

refreshBtn.innerHTML = '<i class="bi bi-arrow-clockwise me-1"></i> Refresh';

refreshBtn.disabled = false;

});

// Update portfolio UI with new data

function updatePortfolioUI(portfolio) {

if (!portfolio) return;

// Update cash balance

cashBalance.textContent = `$${formatNumber(portfolio.cash)}`;

// Update statistics

if (portfolio.stats) {

totalTrades.textContent = portfolio.stats.total_trades;

winRate.textContent = `${formatNumber(portfolio.stats.win_rate)}%`;

const pl = portfolio.stats.profit_loss;

profitLoss.textContent = `$${formatNumber(pl)}`;
profitLoss.className = pl > 0 ? 'metric-value positive' : pl < 0 ? 'metric-value negative'
: 'metric-value';

// Update positions table

if (portfolio.stats && portfolio.stats.active_positions) {

if (portfolio.stats.active_positions.length > 0) {

let positionsHTML = '';

portfolio.stats.active_positions.forEach(position => {

positionsHTML += `

<tr>

<td>${position.symbol}</td>

<td>${position.quantity}</td>

<td>$${formatNumber(position.avg_price)}</td>

<td>$${formatNumber(position.total_cost)}</td>

<td class="text-success">-</td>

</tr>

`;

});

positionsTableBody.innerHTML = positionsHTML;

} else {

positionsTableBody.innerHTML = '<tr><td colspan="5" class="text-center">No


active positions</td></tr>';

// Update trades table


if (portfolio.trades && portfolio.trades.length > 0) {

let tradesHTML = '';

// Show most recent trades first

portfolio.trades.slice().reverse().forEach(trade => {

tradesHTML += `

<tr>

<td>${trade.timestamp}</td>

<td>${trade.symbol}</td>

<td>

<span class="badge ${trade.side === 'buy' ? 'bg-success' : 'bg-danger'}">

${trade.side.toUpperCase()}

</span>

</td>

<td>${trade.quantity}</td>

<td>$${formatNumber(trade.price)}</td>

<td>$${formatNumber(trade.price * trade.quantity)}</td>

<td>${trade.status}</td>

<td>${trade.notes || ''}</td>

</tr>

`;

});

tradesTableBody.innerHTML = tradesHTML;

} else {

tradesTableBody.innerHTML = '<tr><td colspan="8" class="text-center">No trades


found</td></tr>';

}
}

// Utility: Format date and time

function formatDateTime(date) {

if (!(date instanceof Date)) date = new Date(date);

return date.toLocaleString();

// Utility: Format number with commas and decimals

function formatNumber(value) {

return parseFloat(value).toLocaleString(undefined, {

minimumFractionDigits: 2,

maximumFractionDigits: 2

});

});

// AlgoBlocks main JavaScript functionality

document.addEventListener('DOMContentLoaded', function() {

// DOM Elements

const symbolSelect = document.getElementById('symbol');

const startDateInput = document.getElementById('startDate');

const endDateInput = document.getElementById('endDate');

const initialCapitalInput = document.getElementById('initialCapital');

const runBacktestBtn = document.getElementById('runBacktestBtn');

const paperTradeBtn = document.getElementById('paperTradeBtn');

const saveStrategyBtn = document.getElementById('saveStrategyBtn');

const loadStrategyBtn = document.getElementById('loadStrategyBtn');


const clearStrategyBtn = document.getElementById('clearStrategyBtn');

const strategyCanvas = document.getElementById('strategyCanvas');

const performanceMetrics = document.getElementById('performanceMetrics');

const tradesTableBody = document.getElementById('tradesTableBody');

const marketStatus = document.getElementById('marketStatus');

const symbolSearch = document.getElementById('symbolSearch');

const searchSymbolBtn = document.getElementById('searchSymbolBtn');

// Block management

let nextBlockId = 1;

let blocks = [];

let selectedBlock = null;

let chartInstance = null;

// Initialize date inputs with reasonable defaults

const today = new Date();

const oneYearAgo = new Date();

oneYearAgo.setFullYear(today.getFullYear() - 1);

startDateInput.value = formatDate(oneYearAgo);

endDateInput.value = formatDate(today);

// Initialize TradingView Chart

const tradingViewChart = new TradingView.widget({

container_id: 'tradingViewChart',

symbol: 'AAPL',

interval: 'D',

timezone: 'Etc/UTC',

theme: 'light',
style: '1',

locale: 'en',

toolbar_bg: '#f1f3f6',

enable_publishing: false,

hide_top_toolbar: false,

hide_legend: false,

save_image: false,

height: '100%',

width: '100%'

});

// Fetch market status

fetchMarketStatus();

// Set up drag and drop functionality

setupDragAndDrop();

// Set up button event listeners

runBacktestBtn.addEventListener('click', runBacktest);

paperTradeBtn.addEventListener('click', startPaperTrading);

saveStrategyBtn.addEventListener('click', saveStrategy);

loadStrategyBtn.addEventListener('click', loadStrategy);

clearStrategyBtn.addEventListener('click', clearStrategy);

symbolSelect.addEventListener('change', updateSymbol);

searchSymbolBtn.addEventListener('click', searchSymbols);

// Symbol search event

symbolSearch.addEventListener('keyup', function(e) {
if (e.key === 'Enter') {

searchSymbols();

});

// Block configuration modal elements and event listener

const blockConfigModal = new


bootstrap.Modal(document.getElementById('blockConfigModal'));

const blockConfigBody = document.getElementById('blockConfigBody');

const saveBlockConfigBtn = document.getElementById('saveBlockConfigBtn');

saveBlockConfigBtn.addEventListener('click', saveBlockConfig);

// Search for symbols

function searchSymbols() {

const query = symbolSearch.value.trim();

if (query.length < 1) return;

// Show loading in symbol select

symbolSelect.innerHTML = '<option value="">Loading...</option>';

fetch(`/api/search-symbols?query=${encodeURIComponent(query)}&per_page=100`)

.then(response => response.json())

.then(data => {

if (data.error) {

console.error("Symbol search error:", data.error);

return;

}
// Update symbol select with results

symbolSelect.innerHTML = '';

if (data.symbols && data.symbols.length > 0) {

data.symbols.forEach(item => {

const option = document.createElement('option');

option.value = item.symbol;

option.textContent = `${item.symbol} - ${item.name || ''}`;

symbolSelect.appendChild(option);

});

// Update chart with first symbol

updateSymbol();

} else {

symbolSelect.innerHTML = '<option value="">No results found</option>';

})

.catch(error => {

console.error('Error searching symbols:', error);

symbolSelect.innerHTML = '<option value="">Error loading symbols</option>';

});

// Fetch market status periodically

function fetchMarketStatus() {

fetch('/api/markets')

.then(response => response.json())

.then(data => {

if (data.is_open) {
marketStatus.innerHTML = 'Market Status: <span class="text-
success">Open</span>';

} else {

const nextOpen = new Date(data.next_open);

marketStatus.innerHTML = `Market Status: <span class="text-


danger">Closed</span> (Opens ${formatDateTime(nextOpen)})`;

})

.catch(error => {

console.error('Error fetching market status:', error);

marketStatus.textContent = 'Market Status: Unknown';

});

// Update TradingView chart symbol

function updateSymbol() {

const symbol = symbolSelect.value;

if (symbol) {

tradingViewChart.setSymbol(symbol);

// Setup drag and drop functionality

function setupDragAndDrop() {

const draggableBlocks = document.querySelectorAll('.block');

// Make blocks draggable

draggableBlocks.forEach(block => {

block.addEventListener('dragstart', handleDragStart);
});

// Setup canvas as drop target

strategyCanvas.addEventListener('dragover', handleDragOver);

strategyCanvas.addEventListener('drop', handleDrop);

// Handle drag start

function handleDragStart(e) {

e.dataTransfer.setData('text/plain', JSON.stringify({

type: this.dataset.type,

indicatorType: this.dataset.indicatorType || null

}));

// Handle drag over

function handleDragOver(e) {

e.preventDefault();

// Handle drop

function handleDrop(e) {

e.preventDefault();

const canvasRect = strategyCanvas.getBoundingClientRect();

const x = e.clientX - canvasRect.left;

const y = e.clientY - canvasRect.top;

try {
const data = JSON.parse(e.dataTransfer.getData('text/plain'));

createBlock(data.type, data.indicatorType, x, y);

} catch (err) {

console.error('Error parsing drag data:', err);

// Create a block on the canvas

function createBlock(type, indicatorType, x, y) {

const blockId = `block-${nextBlockId++}`;

const block = document.createElement('div');

block.id = blockId;

block.className = `canvas-block ${type}`;

block.style.left = `${x}px`;

block.style.top = `${y}px`;

let blockData = {

id: blockId,

type: type,

x: x,

y: y

};

let blockTitle, blockSettings;

if (type === 'indicator' && indicatorType) {

blockData.indicatorType = indicatorType;

switch (indicatorType) {
case 'SMA':

blockData.period = 20;

blockTitle = 'Simple Moving Average (SMA)';

blockSettings = `Period: ${blockData.period}`;

break;

case 'EMA':

blockData.period = 20;

blockTitle = 'Exponential Moving Average (EMA)';

blockSettings = `Period: ${blockData.period}`;

break;

case 'RSI':

blockData.period = 14;

blockTitle = 'Relative Strength Index (RSI)';

blockSettings = `Period: ${blockData.period}`;

break;

case 'MACD':

blockData.fastPeriod = 12;

blockData.slowPeriod = 26;

blockData.signalPeriod = 9;

blockTitle = 'MACD';

blockSettings = `Fast: ${blockData.fastPeriod}, Slow: ${blockData.slowPeriod},


Signal: ${blockData.signalPeriod}`;

break;

default:

blockTitle = indicatorType;

blockSettings = '';

} else if (type === 'entry') {


blockData.conditions = [];

blockTitle = 'Entry Rule';

blockSettings = 'No conditions set';

} else if (type === 'exit') {

blockData.conditions = [];

blockTitle = 'Exit Rule';

blockSettings = 'No conditions set';

block.innerHTML = `

<div class="block-header">

<span>${blockTitle}</span>

<span class="block-remove" data-id="${blockId}">&times;</span>

</div>

<div class="block-content">

<div class="block-settings">${blockSettings}</div>

</div>

`;

// Make the block draggable

block.draggable = true;

block.addEventListener('dragstart', function(e) {

// Store the current position

const rect = this.getBoundingClientRect();

const offsetX = e.clientX - rect.left;

const offsetY = e.clientY - rect.top;

e.dataTransfer.setData('text/plain', JSON.stringify({
id: this.id,

offsetX: offsetX,

offsetY: offsetY

}));

});

// Add click event to configure the block

block.addEventListener('click', function() {

const id = this.id;

const block = blocks.find(b => b.id === id);

if (block) {

selectedBlock = block;

showBlockConfig(block);

});

// Add remove button functionality

block.querySelector('.block-remove').addEventListener('click', function(e) {

e.stopPropagation();

removeBlock(this.dataset.id);

});

strategyCanvas.appendChild(block);

blocks.push(blockData);

// Remove the placeholder if it exists

const placeholder = strategyCanvas.querySelector('.canvas-placeholder');

if (placeholder) {
placeholder.style.display = 'none';

// Show block configuration modal

function showBlockConfig(block) {

let content = '';

// Generate form based on block type

if (block.type === 'indicator') {

const indicatorType = block.indicatorType;

if (indicatorType === 'SMA' || indicatorType === 'EMA' || indicatorType === 'RSI') {

content = `

<div class="mb-3">

<label class="form-label">Period</label>

<input type="number" class="form-control" id="config-period"


value="${block.period}">

</div>

`;

} else if (indicatorType === 'MACD') {

content = `

<div class="mb-3">

<label class="form-label">Fast Period</label>

<input type="number" class="form-control" id="config-fast-period"


value="${block.fastPeriod}">

</div>

<div class="mb-3">

<label class="form-label">Slow Period</label>


<input type="number" class="form-control" id="config-slow-period"
value="${block.slowPeriod}">

</div>

<div class="mb-3">

<label class="form-label">Signal Period</label>

<input type="number" class="form-control" id="config-signal-period"


value="${block.signalPeriod}">

</div>

`;

} else if (block.type === 'entry' || block.type === 'exit') {

// For simplicity, we'll allow only one condition in this example

// In a real application, you might want to support multiple conditions

let indicator = '';

let operator = '>';

let value = '';

if (block.conditions && block.conditions.length > 0) {

indicator = block.conditions[0].indicator || '';

operator = block.conditions[0].operator || '>';

value = block.conditions[0].value || '';

// Generate indicator options from existing blocks

let indicatorOptions = '';

blocks.filter(b => b.type === 'indicator').forEach(b => {

let indicatorId = '';

if (b.indicatorType === 'SMA' || b.indicatorType === 'EMA') {


indicatorId = `${b.indicatorType}_${b.period}`;

} else if (b.indicatorType === 'RSI') {

indicatorId = `RSI_${b.period}`;

} else if (b.indicatorType === 'MACD') {

indicatorId = 'MACD';

if (indicatorId) {

indicatorOptions += `<option value="${indicatorId}" ${indicator === indicatorId ?


'selected' : ''}>${indicatorId}</option>`;

});

// Add price options

const priceOptions = `

<option value="close" ${indicator === 'close' ? 'selected' : ''}>Close Price</option>

<option value="open" ${indicator === 'open' ? 'selected' : ''}>Open Price</option>

<option value="high" ${indicator === 'high' ? 'selected' : ''}>High Price</option>

<option value="low" ${indicator === 'low' ? 'selected' : ''}>Low Price</option>

`;

content = `

<div class="mb-3">

<label class="form-label">Indicator</label>

<select class="form-select" id="config-indicator">

<option value="">Select an indicator</option>

${priceOptions}

${indicatorOptions}
</select>

</div>

<div class="mb-3">

<label class="form-label">Operator</label>

<select class="form-select" id="config-operator">

<option value=">" ${operator === '>' ? 'selected' : ''}>Greater than (>)</option>

<option value=">=" ${operator === '>=' ? 'selected' : ''}>Greater than or equal


to (>=)</option>

<option value="<" ${operator === '<' ? 'selected' : ''}>Less than (<)</option>

<option value="<=" ${operator === '<=' ? 'selected' : ''}>Less than or equal to


(<=)</option>

<option value="==" ${operator === '==' ? 'selected' : ''}>Equal to (==)</option>

</select>

</div>

<div class="mb-3">

<label class="form-label">Value</label>

<input type="text" class="form-control" id="config-value" value="${value}">

<small class="form-text text-muted">Enter a number or another indicator


ID</small>

</div>

`;

// Update modal content and show it

blockConfigBody.innerHTML = content;

blockConfigModal.show();

// Save block configuration


function saveBlockConfig() {

if (!selectedBlock) return;

// Update block based on type

if (selectedBlock.type === 'indicator') {

if (selectedBlock.indicatorType === 'SMA' || selectedBlock.indicatorType === 'EMA' ||


selectedBlock.indicatorType === 'RSI') {

const period = parseInt(document.getElementById('config-period').value) || 14;

selectedBlock.period = period;

// Update block display

const blockElement = document.getElementById(selectedBlock.id);

blockElement.querySelector('.block-settings').textContent = `Period: ${period}`;

} else if (selectedBlock.indicatorType === 'MACD') {

const fastPeriod = parseInt(document.getElementById('config-fast-period').value) ||


12;

const slowPeriod = parseInt(document.getElementById('config-slow-period').value)


|| 26;

const signalPeriod = parseInt(document.getElementById('config-signal-


period').value) || 9;

selectedBlock.fastPeriod = fastPeriod;

selectedBlock.slowPeriod = slowPeriod;

selectedBlock.signalPeriod = signalPeriod;

// Update block display

const blockElement = document.getElementById(selectedBlock.id);

blockElement.querySelector('.block-settings').textContent = `Fast: ${fastPeriod},


Slow: ${slowPeriod}, Signal: ${signalPeriod}`;
}

} else if (selectedBlock.type === 'entry' || selectedBlock.type === 'exit') {

const indicator = document.getElementById('config-indicator').value;

const operator = document.getElementById('config-operator').value;

const value = document.getElementById('config-value').value;

// Update or create condition

if (indicator) {

if (!selectedBlock.conditions) {

selectedBlock.conditions = [];

if (selectedBlock.conditions.length === 0) {

selectedBlock.conditions.push({ indicator, operator, value });

} else {

selectedBlock.conditions[0] = { indicator, operator, value };

// Update block display

const blockElement = document.getElementById(selectedBlock.id);

blockElement.querySelector('.block-settings').textContent = `${indicator}
${operator} ${value}`;

blockConfigModal.hide();

}
// Remove a block from the canvas

function removeBlock(blockId) {

const blockElement = document.getElementById(blockId);

if (blockElement) {

blockElement.remove();

blocks = blocks.filter(block => block.id !== blockId);

// Show placeholder if no blocks left

if (blocks.length === 0) {

const placeholder = strategyCanvas.querySelector('.canvas-placeholder');

if (placeholder) {

placeholder.style.display = 'block';

// Run backtest with current strategy

function runBacktest() {

if (blocks.length === 0) {

alert('Please add at least one block to create a strategy.');

return;

// Create strategy configuration

const strategy = buildStrategyConfig();


// Show loading state

performanceMetrics.innerHTML = '<p class="text-center"><div class="spinner-border


text-primary" role="status"></div><br>Running backtest...</p>';

// Send to backend for processing

fetch('/api/backtest', {

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: JSON.stringify({

blocks: strategy,

symbol: symbolSelect.value,

startDate: startDateInput.value,

endDate: endDateInput.value,

capital: initialCapitalInput.value

})

})

.then(response => response.json())

.then(results => {

displayBacktestResults(results);

})

.catch(error => {

console.error('Error running backtest:', error);

performanceMetrics.innerHTML = '<p class="text-danger">Error running backtest.


Please try again.</p>';

});

}
// Build strategy configuration from blocks

function buildStrategyConfig() {

// Temporary structure for storing indicators

const indicators = [];

const entryRules = [];

const exitRules = [];

// Process indicator blocks

blocks.filter(block => block.type === 'indicator').forEach(block => {

if (block.indicatorType === 'SMA') {

indicators.push({

type: 'SMA',

parameters: {

period: block.period

});

} else if (block.indicatorType === 'EMA') {

indicators.push({

type: 'EMA',

parameters: {

period: block.period

});

} else if (block.indicatorType === 'RSI') {

indicators.push({

type: 'RSI',

parameters: {

period: block.period
}

});

} else if (block.indicatorType === 'MACD') {

indicators.push({

type: 'MACD',

parameters: {

fast_period: block.fastPeriod,

slow_period: block.slowPeriod,

signal_period: block.signalPeriod

});

});

// Process rule blocks

blocks.filter(block => block.type === 'entry').forEach(block => {

block.conditions.forEach(condition => {

entryRules.push(condition);

});

});

blocks.filter(block => block.type === 'exit').forEach(block => {

block.conditions.forEach(condition => {

exitRules.push(condition);

});

});

return {
indicators: indicators,

entry_rules: entryRules,

exit_rules: exitRules

};

// Display backtest results

function displayBacktestResults(results) {

try {

// Generate HTML for performance metrics

let metricsHtml = '';

if (results.error) {

metricsHtml = `<p class="text-danger">${results.error}</p>`;

} else {

metricsHtml = `

<div class="metric-row">

<span class="metric-label">Initial Capital</span>

<span class="metric-value">$${formatNumber(results.initial_capital)}</span>

</div>

<div class="metric-row">

<span class="metric-label">Final Equity</span>

<span class="metric-value">$${formatNumber(results.final_equity)}</span>

</div>

<div class="metric-row">

<span class="metric-label">Total Return</span>

<span class="metric-value ${results.total_return >= 0 ? 'positive' : 'negative'}">

${formatNumber(results.total_return)}%
</span>

</div>

<div class="metric-row">

<span class="metric-label">Sharpe Ratio</span>

<span class="metric-value">${formatNumber(results.sharpe_ratio)}</span>

</div>

<div class="metric-row">

<span class="metric-label">Max Drawdown</span>

<span class="metric-value
negative">${formatNumber(results.max_drawdown)}%</span>

</div>

<div class="metric-row">

<span class="metric-label">Total Trades</span>

<span class="metric-value">${results.total_trades}</span>

</div>

`;

performanceMetrics.innerHTML = metricsHtml;

// Display trades with error handling

if (results.trades && Array.isArray(results.trades) && results.trades.length > 0) {

let tradesHtml = '';

results.trades.forEach(trade => {

if (!trade.date || !trade.type || !trade.price || !trade.shares) {

console.warn("Incomplete trade data:", trade);

return;
}

tradesHtml += `

<tr>

<td>${trade.date}</td>

<td>${trade.type}</td>

<td>$${formatNumber(trade.price)}</td>

<td>${trade.shares}</td>

<td>$${formatNumber(trade.value || trade.price * trade.shares)}</td>

</tr>

`;

});

if (tradesHtml) {

tradesTableBody.innerHTML = tradesHtml;

} else {

tradesTableBody.innerHTML = '<tr><td colspan="5" class="text-center">No valid


trades generated</td></tr>';

} else {

tradesTableBody.innerHTML = '<tr><td colspan="5" class="text-center">No trades


generated</td></tr>';

// Update equity curve chart

updateEquityCurveChart(results.equity_curve);

} catch (error) {

console.error("Error displaying backtest results:", error);


performanceMetrics.innerHTML = `<p class="text-danger">Error displaying results:
${error.message}</p>`;

tradesTableBody.innerHTML = '<tr><td colspan="5" class="text-center text-


danger">Error displaying trades</td></tr>';

// Update equity curve chart

function updateEquityCurveChart(equityCurve) {

if (!equityCurve || equityCurve.length === 0) {

console.error("No equity curve data available");

return;

// Generate dates for x-axis (improved from just using numbers)

const startDate = new Date(startDateInput.value);

const dates = equityCurve.map((_, index) => {

const date = new Date(startDate);

date.setDate(date.getDate() + index);

return date.toLocaleDateString();

});

// Destroy old chart if exists

if (chartInstance) {

chartInstance.destroy();

try {

// Create new chart with better configuration


const ctx = document.getElementById('equityCurveChart').getContext('2d');

chartInstance = new Chart(ctx, {

type: 'line',

data: {

labels: dates,

datasets: [{

label: 'Portfolio Value',

data: equityCurve,

borderColor: equityCurve[equityCurve.length-1] > equityCurve[0] ? '#28a745' :


'#dc3545',

backgroundColor: 'rgba(0, 123, 255, 0.1)',

fill: true,

tension: 0.1

}]

},

options: {

responsive: true,

maintainAspectRatio: false,

scales: {

y: {

beginAtZero: false,

title: {

display: true,

text: 'Equity ($)'

},

ticks: {

callback: function(value) {

return '$' + value.toLocaleString();


}

},

x: {

title: {

display: true,

text: 'Date'

},

ticks: {

maxTicksLimit: 10

},

plugins: {

legend: {

display: false

},

tooltip: {

callbacks: {

label: function(context) {

return `Equity: $${context.raw.toLocaleString(undefined,


{minimumFractionDigits: 2, maximumFractionDigits: 2})}`;

});
} catch (error) {

console.error("Error rendering chart:", error);

// Start paper trading

function startPaperTrading() {

if (blocks.length === 0) {

alert('Please add at least one block to create a strategy.');

return;

// Fill trade modal with current symbol

document.getElementById('tradeSymbol').value = symbolSelect.value;

// Show paper trade modal

const modal = new bootstrap.Modal(document.getElementById('paperTradeModal'));

modal.show();

// Handle trade submission

document.getElementById('submitTradeBtn').onclick = function() {

const symbol = document.getElementById('tradeSymbol').value;

const quantity = parseInt(document.getElementById('tradeQuantity').value);

const side = document.getElementById('tradeSide').value;

const orderType = document.getElementById('tradeType').value;

const notes = document.getElementById('tradeNotes').value;

// For limit orders, get price


let price = null;

if (orderType === 'limit') {

price = parseFloat(document.getElementById('limitPrice').value);

if (isNaN(price) || price <= 0) {

alert('Please enter a valid limit price');

return;

// Submit paper trade

fetch('/api/paper-trade', {

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: JSON.stringify({

symbol: symbol,

quantity: quantity,

side: side,

orderType: orderType,

price: price,

notes: notes

})

})

.then(response => response.json())

.then(result => {

if (result.error) {

alert(`Error: ${result.error}`);
} else {

alert('Trade executed successfully!');

modal.hide();

})

.catch(error => {

console.error('Error submitting trade:', error);

alert('Error submitting trade. Please try again.');

});

};

// Toggle limit price field based on order type

document.getElementById('tradeType').addEventListener('change', function() {

const limitPriceGroup = document.getElementById('limitPriceGroup');

if (this.value === 'limit') {

limitPriceGroup.classList.remove('d-none');

} else {

limitPriceGroup.classList.add('d-none');

});

// Save current strategy to server

function saveStrategy() {

if (blocks.length === 0) {

alert('Please add at least one block to create a strategy.');

return;

}
const strategyName = prompt('Enter a name for this strategy:');

if (!strategyName) return;

const strategy = {

name: strategyName,

blocks: blocks,

symbol: symbolSelect.value,

date: new Date().toISOString()

};

fetch('/api/save-strategy', {

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: JSON.stringify(strategy)

})

.then(response => response.json())

.then(result => {

if (result.error) {

alert(`Error: ${result.error}`);

} else {

alert(`Strategy "${strategyName}" saved successfully!`);

})

.catch(error => {

console.error('Error saving strategy:', error);


alert('Error saving strategy. Please try again.');

});

// Load a strategy from server

function loadStrategy() {

fetch('/api/list-strategies')

.then(response => response.json())

.then(data => {

if (data.error) {

alert(`Error: ${data.error}`);

return;

const strategies = data.strategies;

if (!strategies || strategies.length === 0) {

alert('No saved strategies found.');

return;

const loadStrategyModal = document.getElementById('loadStrategyModal');

const strategyList = document.getElementById('strategyList');

// Clear previous list

strategyList.innerHTML = '';

// Add strategy items


strategies.forEach(strategy => {

const item = document.createElement('a');

item.href = '#';

item.className = 'list-group-item list-group-item-action';

item.textContent = strategy;

item.onclick = function(e) {

e.preventDefault();

loadStrategyByName(strategy);

bootstrap.Modal.getInstance(loadStrategyModal).hide();

};

strategyList.appendChild(item);

});

// Show modal

const modal = new bootstrap.Modal(loadStrategyModal);

modal.show();

})

.catch(error => {

console.error('Error listing strategies:', error);

alert('Error loading strategies. Please try again.');

});

// Load a specific strategy by name

function loadStrategyByName(strategyName) {

fetch(`/api/load-strategy?name=${encodeURIComponent(strategyName)}`)

.then(response => response.json())

.then(strategyData => {
if (strategyData.error) {

alert(`Error: ${strategyData.error}`);

return;

// Clear current strategy

clearStrategy();

// Restore blocks

if (strategyData.blocks && Array.isArray(strategyData.blocks)) {

strategyData.blocks.forEach(block => {

createBlock(block.type, block.indicatorType, block.x, block.y);

// Restore block properties

const newBlock = blocks[blocks.length - 1];

Object.assign(newBlock, block);

// Update block display

const blockElement = document.getElementById(newBlock.id);

if (blockElement) {

let settingsText = '';

if (block.type === 'indicator') {

if (block.indicatorType === 'SMA' || block.indicatorType === 'EMA' ||


block.indicatorType === 'RSI') {

settingsText = `Period: ${block.period}`;

} else if (block.indicatorType === 'MACD') {

settingsText = `Fast: ${block.fastPeriod}, Slow: ${block.slowPeriod},


Signal: ${block.signalPeriod}`;
}

} else if (block.type === 'entry' || block.type === 'exit') {

if (block.conditions && block.conditions.length > 0) {

const condition = block.conditions[0];

settingsText = `${condition.indicator} ${condition.operator}


${condition.value}`;

} else {

settingsText = 'No conditions set';

blockElement.querySelector('.block-settings').textContent = settingsText;

});

// Update symbol if it was saved

if (strategyData.symbol) {

// Find the option

const option = Array.from(symbolSelect.options).find(opt => opt.value ===


strategyData.symbol);

if (option) {

symbolSelect.value = strategyData.symbol;

updateSymbol();

alert(`Strategy "${strategyName}" loaded successfully.`);

})
.catch(error => {

console.error('Error loading strategy:', error);

alert('Error loading strategy. Please try again.');

});

// Clear current strategy

function clearStrategy() {

// Remove all blocks

blocks.forEach(block => {

const blockElement = document.getElementById(block.id);

if (blockElement) {

blockElement.remove();

});

// Reset blocks array

blocks = [];

// Show placeholder

const placeholder = strategyCanvas.querySelector('.canvas-placeholder');

if (placeholder) {

placeholder.style.display = 'block';

// Clear results

performanceMetrics.innerHTML = '<p class="text-muted text-center"><i class="bi bi-


arrow-clockwise"></i> Run a backtest to see metrics</p>';
tradesTableBody.innerHTML = '<tr><td colspan="5" class="text-center text-muted">No
trades to display</td></tr>';

// Clear chart

if (chartInstance) {

chartInstance.destroy();

chartInstance = null;

// Utility: Format date as YYYY-MM-DD

function formatDate(date) {

const year = date.getFullYear();

const month = String(date.getMonth() + 1).padStart(2, '0');

const day = String(date.getDate()).padStart(2, '0');

return `${year}-${month}-${day}`;

// Utility: Format date and time

function formatDateTime(date) {

return date.toLocaleString();

// Utility: Format number with commas and decimals

function formatNumber(value) {

return parseFloat(value).toLocaleString(undefined, {

minimumFractionDigits: 2,

maximumFractionDigits: 2
});

});

document.addEventListener('DOMContentLoaded', function() {

// Fetch market status

fetchMarketStatus();

// Load tutorial steps

fetch('/api/tutorial/content')

.then(response => response.json())

.then(data => {

const stepsDiv = document.getElementById('tutorialSteps');

stepsDiv.innerHTML = data.map((step, idx) => `

<div class="card mb-3">

<div class="card-header bg-light">

<strong>${idx + 1}. ${step.title}</strong>

</div>

<div class="card-body">${step.body}</div>

</div>

`).join('');

})

.catch(error => {

console.error('Error loading tutorial content:', error);

document.getElementById('tutorialSteps').innerHTML = `

<div class="alert alert-danger">

<i class="bi bi-exclamation-triangle-fill me-2"></i>

Error loading tutorial content. Please try refreshing the page.


</div>

`;

});

// Load terms

fetch('/api/tutorial/terms')

.then(response => response.json())

.then(data => {

const termsDiv = document.getElementById('termsList');

termsDiv.innerHTML = data.map(term => `

<div class="mb-3">

<strong>${term.term}:</strong> ${term.definition}

</div>

`).join('');

})

.catch(error => {

console.error('Error loading terms:', error);

document.getElementById('termsList').innerHTML = `

<div class="alert alert-danger">

<i class="bi bi-exclamation-triangle-fill me-2"></i>

Error loading terms. Please try refreshing the page.

</div>

`;

});

// Fetch market status

function fetchMarketStatus() {

fetch('/api/markets')
.then(response => response.json())

.then(data => {

const marketStatus = document.getElementById('marketStatus');

if (data.is_open) {

marketStatus.innerHTML = 'Market Status: <span class="text-


success">Open</span>';

} else {

const nextOpen = new Date(data.next_open);

marketStatus.innerHTML = `Market Status: <span class="text-


danger">Closed</span> (Opens ${formatDateTime(nextOpen)})`;

})

.catch(error => {

console.error('Error fetching market status:', error);

document.getElementById('marketStatus').textContent = 'Market Status:


Unknown';

});

// Utility: Format date and time

function formatDateTime(date) {

return date.toLocaleString();

});

/* Auth-specific styles */

html,

body {

height: 100%;
}

body {

display: flex;

align-items: center;

padding-top: 40px;

padding-bottom: 40px;

background-color: #f5f5f5;

.form-signin, .form-signup, .form-verify {

width: 100%;

max-width: 330px;

padding: 15px;

margin: auto;

.form-signin .checkbox, .form-signup .checkbox {

font-weight: 400;

.form-signin .form-floating:focus-within,

.form-signup .form-floating:focus-within,

.form-verify .form-floating:focus-within {

z-index: 2;

.form-signin input[type="email"],
.form-signup input[type="text"] {

margin-bottom: -1px;

border-bottom-right-radius: 0;

border-bottom-left-radius: 0;

.form-signin input[type="password"],

.form-signup input[type="email"] {

margin-bottom: -1px;

border-radius: 0;

.form-signup input[type="password"],

.form-verify input[type="text"] {

margin-bottom: 10px;

border-top-left-radius: 0;

border-top-right-radius: 0;

.form-verify input[type="email"] {

margin-bottom: -1px;

border-bottom-right-radius: 0;

border-bottom-left-radius: 0;

.bd-placeholder-img {

font-size: 1.125rem;

text-anchor: middle;
-webkit-user-select: none;

-moz-user-select: none;

user-select: none;

.bi {

color: #0d6efd;

.btn-primary {

margin-top: 10px;

@media (min-width: 768px) {

.bd-placeholder-img-lg {

font-size: 3.5rem;

/* Main styles for AlgoBlocks */

/* General styles */

body {

font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

background-color: #f8f9fa;

/* Sidebar */
.sidebar {

position: fixed;

top: 56px; /* Navbar height */

bottom: 0;

left: 0;

z-index: 100;

padding: 0;

box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);

overflow-y: auto;

.sidebar-heading {

font-size: 0.85rem;

text-transform: uppercase;

/* Block styles */

.block-container {

margin-bottom: 20px;

.block-group {

margin-bottom: 15px;

.block {

padding: 10px;

margin-bottom: 5px;
border-radius: 4px;

cursor: move;

user-select: none;

font-size: 0.9rem;

transition: background-color 0.2s;

.indicator-block {

background-color: #e3f2fd;

border: 1px solid #90caf9;

.indicator-block:hover {

background-color: #bbdefb;

.rule-block {

background-color: #f1f8e9;

border: 1px solid #c5e1a5;

.rule-block:hover {

background-color: #dcedc8;

.entry-block {

border-left: 4px solid #4caf50;

}
.exit-block {

border-left: 4px solid #f44336;

/* Strategy canvas */

.strategy-canvas {

min-height: 300px;

background-color: #fff;

border: 1px dashed #ccc;

border-radius: 4px;

position: relative;

padding: 15px;

margin-bottom: 15px;

.canvas-placeholder {

position: absolute;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

text-align: center;

color: #aaa;

.canvas-placeholder i {

display: block;

margin-bottom: 10px;
}

/* Canvas blocks */

.canvas-block {

position: absolute;

width: 200px;

border-radius: 4px;

background-color: #fff;

box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);

z-index: 10;

cursor: move;

.canvas-block .block-header {

padding: 8px 10px;

display: flex;

justify-content: space-between;

align-items: center;

border-bottom: 1px solid #eee;

font-weight: 600;

font-size: 0.9rem;

.canvas-block .block-content {

padding: 10px;

.canvas-block .block-settings {
font-size: 0.85rem;

color: #555;

.canvas-block .block-remove {

cursor: pointer;

font-size: 1.2rem;

margin-left: 5px;

color: #999;

.canvas-block .block-remove:hover {

color: #f44336;

.canvas-block.indicator {

border-top: 4px solid #2196f3;

.canvas-block.entry {

border-top: 4px solid #4caf50;

.canvas-block.exit {

border-top: 4px solid #f44336;

/* Metrics */
.metric-row {

display: flex;

justify-content: space-between;

margin-bottom: 8px;

padding-bottom: 8px;

border-bottom: 1px solid #f2f2f2;

.metric-row:last-child {

border-bottom: none;

.metric-label {

font-weight: 500;

color: #555;

.metric-value {

font-weight: 600;

.metric-value.positive {

color: #4caf50;

.metric-value.negative {

color: #f44336;

}
/* Responsive adjustments */

@media (max-width: 767.98px) {

.sidebar {

position: static;

height: auto;

.strategy-canvas {

min-height: 200px;

/* Chart responsiveness */

#tradingViewChart {

width: 100%;

border: 1px solid #eee;

.card {

margin-bottom: 20px;

border: none;

box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);

.card-header {

background-color: #f8f9fa;

border-bottom: 1px solid rgba(0, 0, 0, 0.125);


}

.card-title {

margin-bottom: 0;

font-weight: 500;

.table-responsive {

margin-bottom: 0;

/* Navbar customization */

.navbar-brand i {

color: #4285f4;

.nav-link.active {

font-weight: 500;

You might also like