0% found this document useful (0 votes)
10 views102 pages

ALGOblocks 222

The document outlines the structure and code of an algorithmic trading platform called AlgoBlocks, which is built using Flask. It includes user authentication routes, portfolio management, strategy saving and loading functionalities, and various API endpoints for market data and trading operations. The application also features a web interface with HTML templates and static assets for styling and interactivity.
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)
10 views102 pages

ALGOblocks 222

The document outlines the structure and code of an algorithmic trading platform called AlgoBlocks, which is built using Flask. It includes user authentication routes, portfolio management, strategy saving and loading functionalities, and various API endpoints for market data and trading operations. The application also features a web interface with HTML templates and static assets for styling and interactivity.
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/ 102

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

app.py code
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')

os.makedirs('data/saved_strategies', exist_ok=True)
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 (unchanged) ---

@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:

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

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


'us_equity']

return jsonify(symbols[:100])

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()))

return jsonify(symbols[:20])

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'

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

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

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

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/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:

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})

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

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

strategy_data = json.load(f)

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)

index.html code
<!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/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">

<!-- Bootstrap Icons -->

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


icons@1.10.0/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/bootstrap@5.2.3/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>

Login.html code
<!-- login.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/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">

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


icons@1.10.0/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="name@example.com" 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/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>

</html>

Portfolio.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/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">

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


icons@1.10.0/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>

</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> <!-- Will be updated with JS -->

</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/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></scrip
t>

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

</body>

</html>

Register.html

<!-- register.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/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">

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


icons@1.10.0/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="name@example.com" 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/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>

</html>

Tutorial.html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>Tutorial - ALGOblocks</title>

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


<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">

</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>

</div>

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

<div class="card">

<div class="card-header">Stock Market Terms</div>

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

</div>

</div>

</div>

</div>

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

<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>

</html>
Verify.html

<!-- verify.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/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">

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


icons@1.10.0/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="name@example.com" 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/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></scrip
t>

</body>

</html>
Content.json

"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."

]
Terms.json
[

"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."

Utilis/

Init.py

# 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']

Auth_utilis.py

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"

backtest engine.py

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'])

# Debug log available columns

self.logger.info(f"Available columns after indicator calculation: {df.columns.tolist()}")

# Initialize variables for simulation


cash = initial_capital

shares = 0

trades = []

equity_curve = [initial_capital]

# Run simulation

for i in range(len(df)):

if i < 20: # Skip first few rows for indicator calculation

continue

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

price = df['close'].iloc[i]

# Get yesterday's row for signal calculation

yesterday_data = df.iloc[i-1]

# Handle NaN values

if yesterday_data.isnull().any():

continue

# Check entry conditions when we have no position

if shares == 0:

# Check if entry conditions are met

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

if entry_signal:

# Calculate position size

shares_to_buy = int(cash / price)

cost = shares_to_buy * price


if shares_to_buy > 0:

# Record the trade

trades.append({

'date': date,

'type': 'BUY',

'price': price,

'shares': shares_to_buy,

'value': cost

})

# Update portfolio

cash -= cost

shares = shares_to_buy

self.logger.info(f"BUY signal on {date}: {shares} shares at ${price:.2f}")

# Check exit conditions when we have a position

elif shares > 0:

# Check if exit conditions are met

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

if exit_signal:

# Calculate sale value

sale_value = shares * price

# Record the trade

trades.append({

'date': date,

'type': 'SELL',
'price': price,

'shares': shares,

'value': sale_value

})

# Update portfolio

cash += sale_value

shares = 0

self.logger.info(f"SELL signal on {date}: {shares} shares at ${price:.2f}")

# Update equity curve

current_equity = cash + (shares * price)

equity_curve.append(current_equity)

# Calculate performance metrics

initial_equity = equity_curve[0]

final_equity = equity_curve[-1]

total_return = ((final_equity / initial_equity) - 1) * 100

# Calculate other metrics

daily_returns = np.diff(equity_curve) / equity_curve[:-1]

sharpe_ratio = np.mean(daily_returns) / np.std(daily_returns) * np.sqrt(252) if


len(daily_returns) > 0 and np.std(daily_returns) > 0 else 0

max_drawdown = self._calculate_max_drawdown(equity_curve)

self.logger.info(f"Backtest completed: {len(trades)} trades, {total_return:.2f}% return")

# Prepare and return results


return {

'initial_capital': initial_capital,

'final_equity': round(final_equity, 2),

'total_return': round(total_return, 2),

'sharpe_ratio': round(sharpe_ratio, 2),

'max_drawdown': round(max_drawdown, 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()

self.logger.info(f"Calculated SMA_{period}")
elif indicator['type'] == 'EMA':

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

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

self.logger.info(f"Calculated EMA_{period}")

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))

self.logger.info(f"Calculated RSI_{period}")

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']

self.logger.info(f"Calculated MACD with parameters: fast={fast_period},


slow={slow_period}, signal={signal_period}")

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. Available:


{row.index.tolist()}")

continue

# Get the indicator value

indicator_value = row[indicator]

# Handle None or NaN values

if pd.isna(indicator_value):

self.logger.warning(f"Indicator '{indicator}' has NaN 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):

self.logger.warning(f"Could not convert value '{value}' to a number")

continue

# Handle None or NaN values in the comparison value

if pd.isna(compare_value):

self.logger.warning(f"Comparison value '{value}' is NaN")

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)

else:

self.logger.warning(f"Unsupported operator: {operator}")

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

"""

# Convert to numpy array for easier calculations


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

datafetcher.py

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

script.js

// 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');

// 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);

// 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);

// 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;


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.length > 0) {
indicator = block.conditions[0].indicator || '';

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

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

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';

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


'selected' : ''}>${indicatorId}</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>

${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.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) {

// 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

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

let tradesHtml = '';

results.trades.forEach(trade => {

tradesHtml += `

<tr>

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

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

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

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

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

</tr>
`;

});

tradesTableBody.innerHTML = tradesHtml;

} else {

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


generated</td></tr>';

// Update equity curve chart

updateEquityCurveChart(results.equity_curve);

// Update equity curve chart

function updateEquityCurveChart(equityCurve) {

if (!equityCurve || equityCurve.length === 0) return;

// Generate labels (simple count for now)

const labels = Array.from({ length: equityCurve.length }, (_, i) => i);

// Destroy old chart if exists

if (chartInstance) {

chartInstance.destroy();

// Create new chart

const ctx = document.getElementById('equityCurveChart').getContext('2d');

chartInstance = new Chart(ctx, {


type: 'line',

data: {

labels: labels,

datasets: [{

label: 'Equity',

data: equityCurve,

borderColor: '#007bff',

backgroundColor: 'rgba(0, 123, 255, 0.1)',

fill: true,

tension: 0.1

}]

},

options: {

responsive: true,

scales: {

y: {

beginAtZero: false,

title: {

display: true,

text: 'Equity ($)'

},

x: {

title: {

display: true,

text: 'Trading Days'

}
},

plugins: {

legend: {

display: false

},

tooltip: {

callbacks: {

label: function(context) {

return `Equity: $${formatNumber(context.raw)}`;

});

// Start paper trading

function startPaperTrading() {

if (blocks.length === 0) {

alert('Please add at least one block to create a strategy.');

return;

const strategy = buildStrategyConfig();

const symbol = symbolSelect.value;

// For demo purposes, we'll just create a simple buy order


fetch('/api/paper-trade', {

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: JSON.stringify({

symbol: symbol,

quantity: 1, // Just buy 1 share for demo

side: 'buy',

orderType: 'market'

})

})

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

.then(result => {

if (result.error) {

alert(`Error: ${result.error}`);

} else {

alert(`Order submitted! ID: ${result.order_id}`);

})

.catch(error => {

console.error('Error submitting order:', error);

alert('Error submitting order. Please try again.');

});

// Save current strategy to server

function saveStrategy() {
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 => {

const strategies = data.strategies;

if (strategies.length === 0) {

alert('No saved strategies found.');

return;

// Create a simple select dialog

let selectHtml = '<select id="strategySelect" class="form-select mb-3">';

strategies.forEach(strategy => {

selectHtml += `<option value="${strategy}">${strategy}</option>`;

});

selectHtml += '</select>';

// Create a custom modal for selection

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

loadModal.className = 'modal fade';

loadModal.innerHTML = `

<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">

<p>Select a strategy to load:</p>

${selectHtml}

</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="confirmLoadBtn">Load</button>

</div>

</div>

</div>

`;

document.body.appendChild(loadModal);

const loadModalInstance = new bootstrap.Modal(loadModal);

loadModalInstance.show();

// Handle load button click

document.getElementById('confirmLoadBtn').addEventListener('click', function() {

const selectedStrategy = document.getElementById('strategySelect').value;

fetch(`/api/load-strategy?name=${selectedStrategy}`)
.then(response => response.json())

.then(strategyData => {

// 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;

});

loadModalInstance.hide();

})

.catch(error => {

console.error('Error loading strategy:', error);

alert('Error loading strategy. Please try again.');

});

});

// Remove the modal from DOM after hiding

loadModal.addEventListener('hidden.bs.modal', function() {

document.body.removeChild(loadModal);

});

})

.catch(error => {

console.error('Error listing strategies:', error);


alert('Error loading strategies. 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">Run a backtest to see


metrics</p>';

tradesTableBody.innerHTML = '';
// 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

});

});
Tutorial.js

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

// 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>Step ${idx + 1}: ${step.title}</strong>

</div>

<div class="card-body">${step.body}</div>

</div>

`).join('');

});

// 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-2">

<strong>${term.term}:</strong> ${term.definition}

</div>

`).join('');
});

});

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy