"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BTCUSDC Live 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 Live Moving Average and Derivatives with Trading Simulation</h1>
<canvas id="chart" width="800" height="400"></canvas>

<script>
let chart; // Global variable for the chart
let dataFetchInterval; // Interval variable for data fetching

// Function to initialize the default behavior on page load
window.onload = function() {
main();
// Set interval to update data every minute (60000 milliseconds)
dataFetchInterval = setInterval(main, 60000);
};

async function main() {
// Fetch data from 5 hours ago to now
const endTime = new Date().getTime(); // Current time
const startTime = endTime - (8 * 60 * 60 * 1000); // 5 hours ago

// 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 MA99 of the derivative
const ma99Derivative = movingAverage(derivative, 99);

// Calculate derivative of MA99 derivative
const derivativeOfMA99Derivative = firstDerivative(ma99Derivative);

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

// 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 ma99DerivativeData = ma99Derivative.slice(startingIndex);
const derivativeOfMA99DerivativeData = derivativeOfMA99Derivative.slice(startingIndex);

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

// Plot or update the data
plotData(
labels,
priceData,
ma99Data,
derivativeData,
secondDerivativeData,
ma99DerivativeData,
derivativeOfMA99DerivativeData,
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, derivativeOfMA99DerivativeData, ma99Data) {
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 &&
derivativeOfMA99DerivativeData[i] !== null &&
derivativeData[i] < -5 &&
secondDerivativeData[i] > 0 &&
derivativeOfMA99DerivativeData[i] < -0.1 &&
ma99Data[i] > priceData[i]
) {
// Buy signal
if (position === 0 && cash >= 50) {
// Buy BTC worth 50 USDC
let btcBought = 50 / (priceData[i] * 1.0006); // Include 0.06% fee
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] > ma99Data[i]) {
// Sell all BTC holdings
let proceeds = position * (priceData[i] * 0.9994); // Include 0.06% fee
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, ma99DerivativeData, derivativeOfMA99DerivativeData, specialPointsData, totalAssetValueData) {
const ctx = document.getElementById('chart').getContext('2d');

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

if (!chart) {
// Initial chart creation
chart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Actual Price',
data: priceData,
borderColor: 'black',
fill: false,
yAxisID: 'y',
pointRadius: 0,
},
{
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,
},
{
label: 'Derivative Zero Line',
data: zeroData,
borderColor: 'gray',
borderDash: [5, 5],
pointRadius: 0,
borderWidth: 1,
fill: false,
yAxisID: 'y1',
},
{
label: 'MA99 of Derivative',
data: ma99DerivativeData,
borderColor: 'magenta',
fill: false,
yAxisID: 'y1',
pointRadius: 0,
},
{
label: 'Derivative of MA99 of Derivative',
data: derivativeOfMA99DerivativeData,
borderColor: 'cyan',
fill: false,
yAxisID: 'y1',
pointRadius: 0,
},
{
label: 'Special Points (Buy Signal)',
data: specialPointsData,
borderColor: 'purple',
backgroundColor: 'purple',
pointRadius: 5,
pointHoverRadius: 7,
showLine: false,
yAxisID: 'y',
type: 'line',
},
{
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,
},
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: {
callback: function(value, index, ticks) {
return '$' + value.toFixed(2);
}
},
offset: true,
},
}
}
});

// Add double-click event listener to reset zoom
const canvas = document.getElementById('chart');
canvas.addEventListener('dblclick', function(){
chart.resetZoom();
});
} else {
// Update existing chart data
chart.data.labels = labels;
chart.data.datasets[0].data = priceData;
chart.data.datasets[1].data = ma99Data;
chart.data.datasets[2].data = derivativeData;
chart.data.datasets[3].data = secondDerivativeData;
chart.data.datasets[4].data = zeroData;
chart.data.datasets[5].data = ma99DerivativeData;
chart.data.datasets[6].data = derivativeOfMA99DerivativeData;
chart.data.datasets[7].data = specialPointsData;
chart.data.datasets[8].data = totalAssetValueData;
chart.update();
}
}
</script>
</body>
</html>" this is my code for strategy testing. calculate standart deviation with window 99 and draw it to chart too.