ALGOblocks 222
ALGOblocks 222
├── 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
load_dotenv()
app = Flask(__name__)
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')
data_fetcher = DataFetcher(api)
backtest_engine = BacktestEngine(data_fetcher)
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')
if success:
return redirect(url_for('index'))
else:
flash(message, 'danger')
return render_template('login.html')
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')
if success:
return redirect(url_for('verify'))
else:
flash(message, 'danger')
return render_template('register.html')
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')
if success:
return redirect(url_for('login'))
else:
flash(message, 'danger')
return render_template('verify.html')
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('login'))
@app.route('/')
def index():
return redirect(url_for('login'))
return render_template('index.html')
@app.route('/portfolio')
def portfolio():
return redirect(url_for('login'))
email = session['user_email']
portfolio_data = get_user_portfolio(email)
if not portfolio_data:
return redirect(url_for('index'))
@app.route('/tutorial')
def tutorial():
return render_template('tutorial.html')
@app.route('/api/tutorial/content')
def tutorial_content():
return jsonify(json.load(f))
@app.route('/api/tutorial/terms')
def tutorial_terms():
return jsonify(json.load(f))
@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:
def get_available_symbols():
try:
assets = api.list_assets(status='active')
return jsonify(symbols[:100])
except Exception as e:
@app.route('/api/search-symbols', methods=['GET'])
def search_symbols():
try:
assets = api.list_assets(status='active')
symbols = [
return jsonify(symbols[:20])
except Exception as e:
@app.route('/api/historical-data', methods=['GET'])
def get_historical_data():
symbol = request.args.get('symbol', 'AAPL')
try:
return jsonify(data)
except Exception as e:
@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:
@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:
@app.route('/api/backtest', methods=['POST'])
def run_backtest():
try:
if not request.is_json:
strategy_config = request.get_json()
blocks = strategy_config.get('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:
@app.route('/api/paper-trade', methods=['POST'])
def submit_paper_trade():
if not request.is_json:
email = session['user_email']
trade_data = request.json
if success:
return jsonify({
'success': True,
})
else:
@app.route('/api/portfolio/update', methods=['GET'])
def update_portfolio():
email = session['user_email']
portfolio_data = get_user_portfolio(email)
if not portfolio_data:
return jsonify({
'success': True,
'portfolio': portfolio_data
})
@app.route('/api/save-strategy', methods=['POST'])
def save_strategy():
try:
if not request.is_json:
strategy_data = request.get_json()
save_dir = 'data/saved_strategies'
json.dump(strategy_data, f, indent=4)
return jsonify({
"success": True,
"filename": f"{strategy_name}.json"
})
except Exception as e:
@app.route('/api/load-strategy', methods=['GET'])
def load_strategy():
try:
strategy_name = request.args.get('name')
user_email = session['user_email']
if not strategy_name:
strategy_files = os.listdir('data/saved_strategies')
strategies = []
if not filename.endswith('.json'):
continue
try:
strategy_data = json.load(f)
strategies.append(filename.replace('.json', ''))
except:
continue
if not os.path.exists(filepath):
strategy_data = json.load(f)
return jsonify({"error": "You don't have permission to access this strategy"}), 403
return jsonify(strategy_data)
except Exception as e:
if __name__ == '__main__':
app.run(debug=True)
index.html code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<div class="container-fluid">
</a>
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav">
<li class="nav-item">
<li class="nav-item">
</li>
<li class="nav-item">
</li>
</ul>
<li class="nav-item">
</li>
</a>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<span>Strategy Blocks</span>
</h5>
<div class="block-group">
<h6 class="fw-bold">Indicators</h6>
</div>
</div>
</div>
</div>
</div>
<!-- Rules -->
<div class="block-group">
<h6 class="fw-bold">Rules</h6>
</div>
</div>
</div>
</div>
<span>Backtest Settings</span>
</h5>
<div class="mb-3">
</button>
</div>
<select class="form-select" id="symbol">
</select>
</div>
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<div class="input-group">
<span class="input-group-text">$</span>
</div>
</div>
</button>
</button>
</form>
</div>
</div>
</button>
</button>
</button>
</div>
</div>
</div>
<div class="col-12">
<div class="canvas-placeholder">
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="card">
<div class="card-header">
</div>
<div class="card-body">
<div id="performanceMetrics">
</p>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
</div>
<div class="card-body">
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
</div>
<div class="card-body">
<div class="table-responsive">
<thead>
<tr>
<th>Date</th>
<th>Type</th>
<th>Price</th>
<th>Shares</th>
<th>Value</th>
</tr>
</thead>
<tbody id="tradesTableBody">
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body">
</div>
</div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body">
<form id="paperTradeForm">
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<option value="buy">Buy</option>
<option value="sell">Sell</option>
</select>
</div>
<div class="mb-3">
<option value="market">Market</option>
<option value="limit">Limit</option>
</select>
</div>
<div class="input-group">
<span class="input-group-text">$</span>
</div>
</div>
<div class="mb-3">
</div>
</form>
</div>
<div class="modal-footer">
</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="https://cdn.jsdelivr.net/npm/chart.js"></script>
</body>
</html>
Login.html code
<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login - ALGOblocks</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body class="text-center">
<main class="form-signin">
<form method="POST">
{% if messages %}
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="form-floating">
</div>
<div class="form-floating">
<label for="password">Password</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</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">
<title>Portfolio - ALGOblocks</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav">
<li class="nav-item">
</li>
<li class="nav-item">
</li>
</ul>
<li class="nav-item">
</li>
</a>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div class="col-12">
<div class="card">
</button>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
</div>
<div class="card-body">
</div>
</div>
</div>
<div class="col-md-6">
<div class="card mb-3">
</div>
<div class="card-body">
<div class="metric-row">
</div>
<div class="metric-row">
</div>
<div class="metric-row">
<span class="metric-label">Profit/Loss</span>
${{ "%.2f"|format(portfolio.stats.profit_loss) }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
</div>
<div class="card-body">
<div class="table-responsive">
<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 %}
<tr>
</tr>
{% endfor %}
{% else %}
<tr>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
</div>
<div class="card-body">
<div class="table-responsive">
<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 %}
<tr>
<td>
{{ trade.side|upper }}
</span>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
</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>
</body>
</html>
Register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Register - ALGOblocks</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body class="text-center">
<main class="form-signup">
<form method="POST">
{% if messages %}
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="form-floating">
<label for="username">Username</label>
</div>
<div class="form-floating">
</div>
<div class="form-floating">
<label for="password">Password</label>
</div>
</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>
</head>
<body>
<div class="container-fluid">
</a>
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav">
</ul>
<li class="nav-item">
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div class="row">
<div class="col-md-7">
</div>
<div class="col-md-5">
<div class="card">
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></scrip
t>
</body>
</html>
Verify.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body class="text-center">
<main class="form-verify">
<form method="POST">
{% if messages %}
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
</div>
</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
"body": "This tutorial will guide you through building and backtesting trading strategies."
},
"body": "Use the symbol search to select a stock you want to trade."
},
"body": "Drag indicator and rule blocks onto the canvas to build your strategy."
},
"body": "Click on each block to configure its parameters (e.g., period for SMA)."
},
"body": "Click 'Run Backtest' to see how your strategy would have performed."
},
"body": "Use 'Paper Trade' to simulate real trades and track your portfolio."
]
Terms.json
[
"term": "Stock",
},
"term": "Indicator",
"definition": "A mathematical calculation based on price, volume, or open interest, used
to predict market direction."
},
"term": "Backtest",
},
},
"definition": "A graph showing the value of a trading account over time."
Utilis/
Init.py
Auth_utilis.py
import smtplib
import os
import random
import string
import json
VERIFICATION_CODES = {}
USER_DATA_FILE = 'data/users.json'
os.makedirs(os.path.dirname(USER_DATA_FILE), exist_ok=True)
if not os.path.exists(USER_DATA_FILE):
json.dump({}, f)
def load_users():
try:
with open(USER_DATA_FILE, 'r') as f:
return json.load(f)
except:
return {}
def save_users(users):
json.dump(users, f, indent=4)
def generate_verification_code():
return True
message['From'] = smtp_user
message['To'] = email
# Email body
body = f"""
<html>
<body>
</body>
</html>
"""
message.attach(MIMEText(body, 'html'))
try:
server.starttls()
server.login(smtp_user, smtp_password)
server.send_message(message)
server.quit()
return True
except Exception as e:
return False
def register_user(email, password, username):
users = load_users()
if email in users:
code = generate_verification_code()
VERIFICATION_CODES[email] = code
if send_verification_email(email, code):
users[email] = {
'username': username,
'verified': False,
'portfolio': {
'cash': 10000.00,
'trades': []
save_users(users)
else:
users = load_users()
if email in users:
users[email]['verified'] = True
save_users(users)
del VERIFICATION_CODES[email]
else:
"""Log in a user"""
users = load_users()
user = users[email]
if not user['verified']:
session['user_email'] = email
session['username'] = user['username']
def logout_user():
session.pop('user_email', None)
session.pop('username', None)
backtest engine.py
import pandas as pd
import numpy as np
import logging
class BacktestEngine:
"""
Args:
"""
self.data_fetcher = data_fetcher
self.logger = logging.getLogger(__name__)
"""
Args:
Returns:
"""
historical_data = self.data_fetcher.get_historical_data(
symbol=symbol,
timeframe='1D',
return {
'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)
if start_date:
if end_date:
df = self._apply_indicators(df, strategy['indicators'])
shares = 0
trades = []
equity_curve = [initial_capital]
# Run simulation
for i in range(len(df)):
continue
date = df.index[i].strftime('%Y-%m-%d')
price = df['close'].iloc[i]
yesterday_data = df.iloc[i-1]
if yesterday_data.isnull().any():
continue
if shares == 0:
if entry_signal:
trades.append({
'date': date,
'type': 'BUY',
'price': price,
'shares': shares_to_buy,
'value': cost
})
# Update portfolio
cash -= cost
shares = shares_to_buy
if exit_signal:
trades.append({
'date': date,
'type': 'SELL',
'price': price,
'shares': shares,
'value': sale_value
})
# Update portfolio
cash += sale_value
shares = 0
equity_curve.append(current_equity)
initial_equity = equity_curve[0]
final_equity = equity_curve[-1]
max_drawdown = self._calculate_max_drawdown(equity_curve)
'initial_capital': initial_capital,
'total_trades': len(trades),
'trades': trades,
"""
Args:
Returns:
"""
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']
self.logger.info(f"Calculated EMA_{period}")
period = indicator['parameters']['period']
delta = df['close'].diff()
rs = gain / loss
rs = rs.fillna(0)
self.logger.info(f"Calculated RSI_{period}")
fast_period = indicator['parameters']['fast_period']
slow_period = indicator['parameters']['slow_period']
signal_period = indicator['parameters']['signal_period']
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()
except Exception as e:
return df
"""
Args:
Returns:
"""
if not conditions:
return False
results = []
for condition in conditions:
indicator = condition['indicator']
operator = condition['operator']
value = condition['value']
continue
indicator_value = row[indicator]
if pd.isna(indicator_value):
continue
compare_value = None
compare_value = row[value]
else:
if value == 'close':
compare_value = row['close']
compare_value = row['open']
compare_value = row['high']
compare_value = row['low']
else:
try:
compare_value = float(value)
continue
if pd.isna(compare_value):
continue
# Apply operator
try:
if operator == '>':
results.append(indicator_value == compare_value)
elif operator == '>=':
else:
except Exception as e:
continue
if not results:
return False
return all(results)
"""
Args:
Returns:
"""
running_max = np.maximum.accumulate(equity)
drawdowns = np.nan_to_num(drawdowns)
return max_drawdown
datafetcher.py
import pandas as pd
import numpy as np
import logging
class DataFetcher:
self.api = api
self.cache = {} # Simple cache for historical data
self.logger = logging.getLogger(__name__)
"""
Args:
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:
"""
cache_key = f"{symbol}_{timeframe}_{period}"
if cache_key in self.cache:
return data
end_date = datetime.now()
if period == '1D':
else: # '5Y'
start_str = start_date.strftime('%Y-%m-%d')
end_str = end_date.strftime('%Y-%m-%d')
timeframe_map = {
'1Min': '1Min',
'5Min': '5Min',
'15Min': '15Min',
'1H': '1Hour',
'1D': '1Day'
try:
try:
bars = self.api.get_bars(
symbol=symbol,
timeframe=alpaca_timeframe,
start=start_str,
end=end_str,
limit=1000
data = []
data.append({
'open': bar.o,
'high': bar.h,
'low': bar.l,
'close': bar.c,
'volume': bar.v
})
return data
except AttributeError:
bars = self.api.get_barset(
symbols=symbol,
timeframe=timeframe,
start=start_str,
end=end_str,
limit=1000
if symbol in bars:
data = []
data.append({
'open': bar.o,
'high': bar.h,
'low': bar.l,
'close': bar.c,
'volume': bar.v
})
# Cache the results
return data
except Exception as e:
sample_data = self._get_sample_data(symbol)
return sample_data
end_date = datetime.now()
np.random.seed(seed)
# Generate sample data with realistic price action
data = []
current_price = base_price
if trend == 'up':
drift = 0.0005
drift = -0.0005
else:
drift = 0.0
close_price = current_price
close_price = current_price
# Random volume
data.append({
'time': date.strftime('%Y-%m-%d'),
'volume': volume
})
return data
script.js
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
// Block management
let nextBlockId = 1;
oneYearAgo.setFullYear(today.getFullYear() - 1);
startDateInput.value = formatDate(oneYearAgo);
endDateInput.value = formatDate(today);
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%'
});
fetchMarketStatus();
setupDragAndDrop();
runBacktestBtn.addEventListener('click', runBacktest);
paperTradeBtn.addEventListener('click', startPaperTrading);
saveStrategyBtn.addEventListener('click', saveStrategy);
loadStrategyBtn.addEventListener('click', loadStrategy);
clearStrategyBtn.addEventListener('click', clearStrategy);
symbolSelect.addEventListener('change', updateSymbol);
saveBlockConfigBtn.addEventListener('click', saveBlockConfig);
function fetchMarketStatus() {
fetch('/api/markets')
.then(data => {
if (data.is_open) {
} else {
})
.catch(error => {
});
function updateSymbol() {
function setupDragAndDrop() {
draggableBlocks.forEach(block => {
block.addEventListener('dragstart', handleDragStart);
});
strategyCanvas.addEventListener('dragover', handleDragOver);
strategyCanvas.addEventListener('drop', handleDrop);
function handleDragStart(e) {
e.dataTransfer.setData('text/plain', JSON.stringify({
type: this.dataset.type,
}));
function handleDragOver(e) {
e.preventDefault();
}
// Handle drop
function handleDrop(e) {
e.preventDefault();
try {
} catch (err) {
block.id = blockId;
block.style.left = `${x}px`;
block.style.top = `${y}px`;
let blockData = {
id: blockId,
type: type,
x: x,
y: y
};
blockData.indicatorType = indicatorType;
switch (indicatorType) {
case 'SMA':
blockData.period = 20;
break;
case 'EMA':
blockData.period = 20;
break;
case 'RSI':
blockData.period = 14;
break;
case 'MACD':
blockData.fastPeriod = 12;
blockData.slowPeriod = 26;
blockData.signalPeriod = 9;
blockTitle = 'MACD';
break;
default:
blockTitle = indicatorType;
blockSettings = '';
blockData.conditions = [];
blockData.conditions = [];
block.innerHTML = `
<div class="block-header">
<span>${blockTitle}</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) {
e.dataTransfer.setData('text/plain', JSON.stringify({
id: this.id,
offsetX: offsetX,
offsetY: offsetY
}));
});
block.addEventListener('click', function() {
const id = this.id;
if (block) {
selectedBlock = block;
showBlockConfig(block);
});
block.querySelector('.block-remove').addEventListener('click', function(e) {
e.stopPropagation();
removeBlock(this.dataset.id);
});
strategyCanvas.appendChild(block);
blocks.push(blockData);
if (placeholder) {
placeholder.style.display = 'none';
function showBlockConfig(block) {
content = `
<div class="mb-3">
<label class="form-label">Period</label>
`;
content = `
<div class="mb-3">
</div>
<div class="mb-3">
</div>
<div class="mb-3">
</div>
`;
if (block.conditions.length > 0) {
indicator = block.conditions[0].indicator || '';
indicatorId = `${b.indicatorType}_${b.period}`;
indicatorId = `RSI_${b.period}`;
indicatorId = 'MACD';
});
content = `
<div class="mb-3">
<label class="form-label">Indicator</label>
${indicatorOptions}
</select>
</div>
<div class="mb-3">
<label class="form-label">Operator</label>
</select>
</div>
<div class="mb-3">
<label class="form-label">Value</label>
</div>
`;
blockConfigBody.innerHTML = content;
blockConfigModal.show();
function saveBlockConfig() {
if (!selectedBlock) return;
selectedBlock.period = period;
selectedBlock.fastPeriod = fastPeriod;
selectedBlock.slowPeriod = slowPeriod;
selectedBlock.signalPeriod = signalPeriod;
}
} else if (selectedBlock.type === 'entry' || selectedBlock.type === 'exit') {
if (indicator) {
if (selectedBlock.conditions.length === 0) {
} else {
blockElement.querySelector('.block-settings').textContent = `${indicator}
${operator} ${value}`;
blockConfigModal.hide();
function removeBlock(blockId) {
if (blockElement) {
blockElement.remove();
}
if (blocks.length === 0) {
if (placeholder) {
placeholder.style.display = 'block';
function runBacktest() {
if (blocks.length === 0) {
return;
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(results => {
displayBacktestResults(results);
})
.catch(error => {
});
function buildStrategyConfig() {
indicators.push({
type: 'SMA',
parameters: {
period: block.period
});
indicators.push({
type: 'EMA',
parameters: {
period: block.period
});
indicators.push({
type: 'RSI',
parameters: {
period: block.period
});
indicators.push({
type: 'MACD',
parameters: {
fast_period: block.fastPeriod,
slow_period: block.slowPeriod,
signal_period: block.signalPeriod
});
});
block.conditions.forEach(condition => {
entryRules.push(condition);
});
});
block.conditions.forEach(condition => {
exitRules.push(condition);
});
});
return {
indicators: indicators,
entry_rules: entryRules,
exit_rules: exitRules
};
}
function displayBacktestResults(results) {
if (results.error) {
} else {
metricsHtml = `
<div class="metric-row">
<span class="metric-value">$${formatNumber(results.initial_capital)}</span>
</div>
<div class="metric-row">
<span class="metric-value">$${formatNumber(results.final_equity)}</span>
</div>
<div class="metric-row">
${formatNumber(results.total_return)}%
</span>
</div>
<div class="metric-row">
<span class="metric-value">${formatNumber(results.sharpe_ratio)}</span>
</div>
<div class="metric-row">
<span class="metric-value
negative">${formatNumber(results.max_drawdown)}%</span>
</div>
<div class="metric-row">
<span class="metric-value">${results.total_trades}</span>
</div>
`;
performanceMetrics.innerHTML = metricsHtml;
// Display trades
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 {
updateEquityCurveChart(results.equity_curve);
function updateEquityCurveChart(equityCurve) {
if (chartInstance) {
chartInstance.destroy();
data: {
labels: labels,
datasets: [{
label: 'Equity',
data: equityCurve,
borderColor: '#007bff',
fill: true,
tension: 0.1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: false,
title: {
display: true,
},
x: {
title: {
display: true,
}
},
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: function(context) {
});
function startPaperTrading() {
if (blocks.length === 0) {
return;
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
symbol: symbol,
side: 'buy',
orderType: 'market'
})
})
.then(result => {
if (result.error) {
alert(`Error: ${result.error}`);
} else {
})
.catch(error => {
});
function saveStrategy() {
const strategyName = prompt('Enter a name for this strategy:');
if (!strategyName) return;
const strategy = {
name: strategyName,
blocks: blocks,
symbol: symbolSelect.value,
};
fetch('/api/save-strategy', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(strategy)
})
.then(result => {
if (result.error) {
alert(`Error: ${result.error}`);
} else {
})
.catch(error => {
function loadStrategy() {
fetch('/api/list-strategies')
.then(data => {
if (strategies.length === 0) {
return;
strategies.forEach(strategy => {
});
selectHtml += '</select>';
loadModal.innerHTML = `
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body">
${selectHtml}
</div>
<div class="modal-footer">
</div>
</div>
</div>
`;
document.body.appendChild(loadModal);
loadModalInstance.show();
document.getElementById('confirmLoadBtn').addEventListener('click', function() {
fetch(`/api/load-strategy?name=${selectedStrategy}`)
.then(response => response.json())
.then(strategyData => {
clearStrategy();
// Restore blocks
strategyData.blocks.forEach(block => {
Object.assign(newBlock, block);
if (blockElement) {
} else {
blockElement.querySelector('.block-settings').textContent =
settingsText;
});
loadModalInstance.hide();
})
.catch(error => {
});
});
loadModal.addEventListener('hidden.bs.modal', function() {
document.body.removeChild(loadModal);
});
})
.catch(error => {
});
function clearStrategy() {
blocks.forEach(block => {
if (blockElement) {
blockElement.remove();
});
blocks = [];
// Show placeholder
if (placeholder) {
placeholder.style.display = 'block';
// Clear results
tradesTableBody.innerHTML = '';
// Clear chart
if (chartInstance) {
chartInstance.destroy();
chartInstance = null;
function formatDate(date) {
return `${year}-${month}-${day}`;
function formatDateTime(date) {
return date.toLocaleString();
function formatNumber(value) {
return parseFloat(value).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
});
Tutorial.js
document.addEventListener('DOMContentLoaded', function() {
fetch('/api/tutorial/content')
.then(data => {
</div>
<div class="card-body">${step.body}</div>
</div>
`).join('');
});
// Load terms
fetch('/api/tutorial/terms')
.then(data => {
<div class="mb-2">
<strong>${term.term}:</strong> ${term.definition}
</div>
`).join('');
});
});