"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BTCUSDC Moving Average and Derivatives with Trading Simulation</title>
<!-- Include Chart.js (Latest Version) -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Include Chart.js Zoom Plugin (Latest Version) -->
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
</head>
<body>
<h1>BTCUSDC Moving Average and Derivatives with Trading Simulation</h1>
<label for="startDate">Start Date:</label>
<input type="date" id="startDate" value="2023-11-01">
<label for="endDate">End Date:</label>
<input type="date" id="endDate" value="2023-11-05">
<button id="fetchData">Fetch Data</button>
<canvas id="chart" width="800" height="400"></canvas>

<script>
// Function to initialize the default behavior on page load
window.onload = function() {
main();
};

document.getElementById('fetchData').addEventListener('click', function() {
main();
});

async function main() {
const startDateInput = document.getElementById('startDate').value;
const endDateInput = document.getElementById('endDate').value;

// Convert dates to Unix timestamps in milliseconds
const startTime = new Date(startDateInput).getTime();
const endTime = new Date(endDateInput).getTime();

// Fetch BTCUSDC 1-minute prices from Binance API
const symbol = 'BTCUSDC';
const interval = '1m';

const klines = await fetchKlines(symbol, interval, startTime, endTime);

if (klines.length === 0) {
alert('No data available for the selected date range.');
return;
}

// Extract closing prices and timestamps
const timestamps = klines.map(k => k[0]); // Open time
const closePrices = klines.map(k => parseFloat(k[4])); // Close price

// Calculate 99-point moving average
const ma99 = movingAverage(closePrices, 99);

// Calculate first derivative of MA99
const derivative = firstDerivative(ma99);

// Calculate second derivative of MA99
const secondDerivative = firstDerivative(derivative);

// Calculate MA20 of the derivative
const ma20Derivative = movingAverage(derivative, 99);

// Calculate derivative of MA20 derivative
const derivativeOfMA20Derivative = firstDerivative(ma20Derivative);

// The earliest index where all data arrays have valid data
const startingIndex = 119;

// Prepare data for plotting
const labels = timestamps.slice(startingIndex).map(ts => new Date(ts).toLocaleString());
const priceData = closePrices.slice(startingIndex);
const ma99Data = ma99.slice(startingIndex);
const derivativeData = derivative.slice(startingIndex);
const secondDerivativeData = secondDerivative.slice(startingIndex);
const ma20DerivativeData = ma20Derivative.slice(startingIndex);
const derivativeOfMA20DerivativeData = derivativeOfMA20Derivative.slice(startingIndex);

// Simulate trading strategy
const {specialPointsData, tradeSignals, totalAssetValueData} = simulateTrading(
priceData,
derivativeData,
secondDerivativeData,
derivativeOfMA20DerivativeData,ma99Data
);

// Plot the data
plotData(
labels,
priceData,
ma99Data,
derivativeData,
secondDerivativeData,
ma20DerivativeData,
derivativeOfMA20DerivativeData,
specialPointsData,
totalAssetValueData
);
}

async function fetchKlines(symbol, interval, startTime, endTime) {
const limit = 1000; // Max limit per request
let klines = [];
let fetchTime = startTime;

while (fetchTime < endTime) {
const url = `https://fapi.binance.com/fapi/v1/klines?symbol=${symbol}&interval=${interval}&startTime=${fetchTime}&endTime=${endTime}&limit=${limit}`;

const response = await fetch(url);
const data = await response.json();

if (data.code) {
// Handle error
alert('Error fetching data: ' + data.msg);
break;
}

if (data.length === 0) {
break;
}

klines = klines.concat(data);
fetchTime = data[data.length - 1][0] + 60000; // Next minute
}

return klines;
}

function movingAverage(data, windowSize) {
let ma = [];
for (let i = 0; i < data.length; i++) {
if (i < windowSize - 1 || data[i] === null) {
ma.push(null); // Not enough data to calculate MA
} else {
const windowData = data.slice(i - windowSize + 1, i + 1);
if (windowData.includes(null)) {
ma.push(null); // Moving average cannot be calculated with null values
} else {
const sum = windowData.reduce((acc, val) => acc + val, 0);
ma.push(sum / windowSize);
}
}
}
return ma;
}

function firstDerivative(data) {
let derivative = [];
for (let i = 0; i < data.length; i++) {
if (i === 0 || data[i - 1] === null || data[i] === null) {
derivative.push(null); // Cannot calculate derivative
} else {
derivative.push(data[i] - data[i - 1]);
}
}
return derivative;
}

function simulateTrading(priceData, derivativeData, secondDerivativeData, derivativeOfMA20DerivativeData,ma99) {
let specialPointsData = [];
let tradeSignals = [];
let position = 0; // BTC holdings
let cash = 500; // USDC balance (we start with 500 USDC)
let totalAssetValueData = []; // Total value of assets over time in USDC

for (let i = 0; i < priceData.length; i++) {
// Buy condition with new criterion
if (
derivativeData[i] !== null &&
secondDerivativeData[i] !== null &&
derivativeOfMA20DerivativeData[i] !== null &&
derivativeData[i] < -5 &&
secondDerivativeData[i] > 0 &&
derivativeOfMA20DerivativeData[i] < -0.1 &&
ma99[i] > priceData[i]
) {
// Buy signal
if (position === 0 && cash >= 50) {
// Buy BTC worth 50 USDC
let btcBought = 50 / (priceData[i]+40);
position += btcBought;
cash -= 50; // We spend 50 USDC
tradeSignals.push({type: 'buy', index: i, price: priceData[i]});
}
specialPointsData.push(priceData[i]); // Mark special point
} else {
specialPointsData.push(null);
}

// Sell condition
if (position > 0 && priceData[i] > ma99[i]) {
// Sell all BTC holdings
let proceeds = position *(
priceData[i]-40);
cash += proceeds;
position = 0; // Reset position

tradeSignals.push({type: 'sell', index: i, price: priceData[i]});
}

// Update total asset value
let totalAssetValue = cash + position * priceData[i];
totalAssetValueData.push(totalAssetValue);
}

return {specialPointsData, tradeSignals, totalAssetValueData};
}

function plotData(labels, priceData, ma99Data, derivativeData, secondDerivativeData, ma20DerivativeData, derivativeOfMA20DerivativeData, specialPointsData, totalAssetValueData) {
const ctx = document.getElementById('chart').getContext('2d');

// Create an array of zeros for the zero line
const zeroData = labels.map(() => 0);

// Clear existing chart if any
if (window.myChart instanceof Chart) {
window.myChart.destroy();
}

// Register the zoom plugin (necessary in Chart.js v4)
Chart.register(ChartZoom);

window.myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Actual Price',
data: priceData,
borderColor: 'black',
fill: false,
yAxisID: 'y',
pointRadius: 0, // Prevent default points
},
{
label: 'Moving Average (99)',
data: ma99Data,
borderColor: 'blue',
fill: false,
yAxisID: 'y',
pointRadius: 0,
},
{
label: 'First Derivative',
data: derivativeData,
borderColor: 'green',
fill: false,
yAxisID: 'y1',
pointRadius: 0,
},
{
label: 'Second Derivative',
data: secondDerivativeData,
borderColor: 'red',
fill: false,
yAxisID: 'y1',
pointRadius: 0,
},
// Add the zero line for derivative axis
{
label: 'Derivative Zero Line',
data: zeroData,
borderColor: 'gray',
borderDash: [5, 5],
pointRadius: 0,
borderWidth: 1,
fill: false,
yAxisID: 'y1',
},
// New datasets for MA20 of derivative and its derivative
{
label: 'MA20 of Derivative',
data: ma20DerivativeData,
borderColor: 'magenta',
fill: false,
yAxisID: 'y1',
pointRadius: 0,
},
{
label: 'Derivative of MA20 of Derivative',
data: derivativeOfMA20DerivativeData,
borderColor: 'cyan',
fill: false,
yAxisID: 'y1',
pointRadius: 0,
},
// New dataset for special points
{
label: 'Special Points (Buy Signal)',
data: specialPointsData,
borderColor: 'purple',
backgroundColor: 'purple',
pointRadius: 5, // Size of the dot
pointHoverRadius: 7,
showLine: false,
yAxisID: 'y',
type: 'line', // Ensure it aligns with the chart type
},
// Dataset for total asset value
{
label: 'Total Asset Value',
data: totalAssetValueData,
borderColor: 'orange',
fill: false,
yAxisID: 'y2',
pointRadius: 0,
borderWidth: 2,
tension: 0.1,
},
]
},
options: {
responsive: true,
interaction: {
mode: 'index',
intersect: false,
},
stacked: false,
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x',
modifierKey: null, // Allows panning without modifier key
},
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true,
},
mode: 'x',
},
},
},
scales: {
x: {
display: true,
ticks: {
maxRotation: 90,
minRotation: 90,
maxTicksLimit: 20,
}
},
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: 'Price / Moving Average'
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
grid: {
drawOnChartArea: false,
},
title: {
display: true,
text: 'Derivatives'
}
},
y2: {
type: 'linear',
display: true,
position: 'right',
grid: {
drawOnChartArea: false,
},
title: {
display: true,
text: 'Total Asset Value (USDC)'
},
ticks: {
// Include USDC symbol
callback: function(value, index, ticks) {
return '$' + value.toFixed(2);
}
},
// Offset the scale to the right
offset: true,
},
}
}
});

// Add double-click event listener to reset zoom
const canvas = document.getElementById('chart');
canvas.addEventListener('dblclick', function(){
window.myChart.resetZoom();
});
}

// Automatically fetch data on page load
// (Already called inside window.onload)
</script>
</body>
</html>" this is my btcusdc strategy code, i want that cancel drawing of second derivative. also, calculate the moving average 20 of derivativeOfMA20DerivativeData. draw it to too.