Skip to content
Snippets Groups Projects
Commit 2d19b1b3 authored by benjamin.anthonio's avatar benjamin.anthonio
Browse files

feat: graphic visualisation of datas

parent bf354cbc
Branches
No related tags found
No related merge requests found
Showing
with 272 additions and 29 deletions
......@@ -4,5 +4,8 @@
| 28.10.2024-04.11.2024 | Lister les fonctionnalités nécessaires au projet + Lire la documentation de Mr Souzaluz | Lecture des parties "Lexique", "Liste des acronymes", "1.2 TRADING AUTOMATISÉ", "CHAPITRE 2 : LES STRATÉGIES" et "CHAPITRE 3 : MÉTHODOLOGIE" + Début du listage des fonctionnalités | | |
| 04.11.2024-11.11.2024 | Rechercher une API qui permet de récupérer les données de chaque seconde depuis 4-5 ans + Justifier le choix de la technologie | Listage des différentes API avec leurs avantages et désavantages + Justification de la technologie + Trouvé une librairie pour les graphiques | La plupart des API ont des limites de données par requêtes et de fréquences des données (1 min) | |
| 11.11.2024-18.11.2024 | Récupérer les données (200000) et tester la librairie pour faire des graphes | J'ai créer un projet spring boot qui permet de récupérer les données en faisant plusieurs appels à l'api Binance | Problème avec la librairie des graphiques : le graphique s'affiche mais pas les candles | |
| 18.11.2024-26.11.2024 | Afficher les données avec la librairie + Voir le temps que prends le programme pour récupérer 1 an de données + Mise en cache des données si récupération trop longue | Affichage des données avec la librairie. Il faut environ 1 minute pour récupérer 1 an de données. L'utilisateur peut choisir les dates qu'il veut. | Affichage des données pour plus de 10 jours : problème de chevauchement des timestamp de la 1ère requête et de la 2ème | |
| 26.11.2024-02.12.2024 | Commencer à implémenter le simulateur et commencer l'introduction | Début d'une introduction et implémentation d'un ticker (déclencher un évènement toutes les $x$ secondes) | | |
<!-- | | | | | | -->
\ No newline at end of file
# Projet de semestre
## Introduction
Le trading de cryptomonnaies se démocratise de plus en plus notamment grâce à la multiplication des plateformes de trading comme Binance, Coinbase ou encore Crypto\.com. Ces plateformes permettent d'acheter et de vendre des cryptomonnaies assez facilement notamment grâce à leurs applictions sur smartphone. De plus, beaucoup de personnes remarquent la grande volatilité des prix des cryptomonnaies et font du profit grâce à ces variations de prix. Cependant, bien que certains fassent effectivement des bénéfices, d'autres font de grosses pertes. Et c'est pourquoi les simulateurs de trading de cryptomonnaies sont utiles. Ces simulateurs permettent de s'entrainer avec les stratégies existantes sans pour autant avoir à réellement investir dans une cryptomonnaie et risquer de tout perdre.
Les simulateurs de trading de cryptomonnaies sont très utiles pour tester et améliorer les différents outils et stratégies de trading de cryptomonnaies. Ce projet a pour but de refactoriser un simulateur de trading de cryptomonnaies existant, développé en Python, dans un langage orienté objet.
Ce projet est la suite de projets déjà réalisés par Mr. Pighini, Mr. Toniutt et Mr. SouzaLuz. Il est réalisé dans le cadre des projets de semestre et de Bachelor à HEPIA et a pour objectif d'améliorer le simulateur de trading de cryptomonnaies existant, notamment grâce à des notions de développement logicielle comme la programmation orientée objet.
## Cahier des charges
- Choisir une API permettant de récupérer les données de cryptomonnaies depuis au moins 5ans.
- Afficher un graphique des bougies d'une cryptomonnaie sur une certaine période
- Créer le simulateur permettant de simuler l'évolution du prix d'une cryptomonnaie toutes les $x$ secondes
- Implémenter les différents indicateurs
- Créer et gérer un système de cache pour les données (moins important pour le moment)
......@@ -60,10 +60,6 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
......
package com.example.testapi.controller;
import com.example.testapi.model.DateRange;
import com.example.testapi.service.ApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.Map;
@RestController
@RequestMapping("/api")
......@@ -13,6 +14,8 @@ public class ApiController {
private final ApiService apiService;
private DateRange dateRange;
@Autowired
public ApiController(ApiService apiService) {
this.apiService = apiService;
......@@ -22,8 +25,16 @@ public class ApiController {
public String getGraphPage() throws InterruptedException, com.fasterxml.jackson.core.JsonProcessingException {
System.out.println("btc to usdt graphique !");
// System.out.println(apiService.getCandlesJson());
return apiService.getCandlesJson();
// return "ok";
// return "ca marche";
return apiService.getCandlesJson(dateRange.getTimestampStartDate(), dateRange.getTimestampEndDate());
}
@PostMapping("/sendDates")
public Map<String, Object> sendDates(@RequestBody DateRange json) throws InterruptedException, com.fasterxml.jackson.core.JsonProcessingException {
System.out.println("Dates received: " + json.getStartDate() + " - " + json.getEndDate());
this.dateRange = new DateRange(json.getStartDate(), json.getEndDate());
return Collections.emptyMap();
}
}
......@@ -19,4 +19,10 @@ public class PageApiController {
System.out.println("home page requested !");
return "index.html";
}
@GetMapping("/candlesGraph")
public String getGraphPage() {
System.out.println("graph page requested !");
return "affichageDataGraph.html";
}
}
package com.example.testapi.model;
import java.time.LocalDate;
import java.time.ZoneId;
public class DateRange {
private String startDate;
private String endDate;
public DateRange(String startDate, String endDate) {
this.startDate = startDate;
this.endDate = endDate;
}
public String getStartDate() {
return startDate;
}
public void setStartDate(String startDate) {
this.startDate = startDate;
}
public String getEndDate() {
return endDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
public long getTimestampStartDate() {
return this.dateToTimestamp(this.startDate);
}
public long getTimestampEndDate() {
return this.dateToTimestamp(this.endDate);
}
public long dateToTimestamp(String date) {
LocalDate localDate = LocalDate.parse(date);
return localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
}
......@@ -25,18 +25,28 @@ public class ApiService {
private String candlesJson;
public void getCandles() throws InterruptedException, com.fasterxml.jackson.core.JsonProcessingException {
String baseUrl = "https://api4.binance.com/api/v3/klines";
long startTime = 1572331210000L;
long endTime = startTime + (15 * 1000 * 60 * 1000);
private static final String baseUrl = "https://api4.binance.com/api/v3/klines";
private static final long TIME_10_JOURS = 10 * 24 * 60 * 60 * 1000;
public void getCandles(long startTime, long endTime) throws InterruptedException, com.fasterxml.jackson.core.JsonProcessingException {
if (endTime < startTime) {
System.out.println("La date de fin ne peut pas être avant la date de début");
return;
}
List<Candle> candles = new ArrayList<Candle>();
for (int i = 0; i < 200; i++)
int nbreq = 0;
while (startTime < endTime)
{
// System.out.println("Nb req API binance : " + nbreq);
// nbreq++;
long currEndTime = Math.min(startTime + TIME_10_JOURS + 1, endTime);
String url = UriComponentsBuilder.fromHttpUrl(baseUrl)
.queryParam("symbol", "BTCUSDT")
.queryParam("interval", "15m")
.queryParam("startTime", startTime)
.queryParam("limit", 1000) // limite à 1 pour tester plus vite mais changer par 1000 après
.queryParam("endTime", currEndTime)
.queryParam("limit", 1000)
.toUriString();
String response = restTemplate.getForObject(url, String.class);
......@@ -56,20 +66,25 @@ public class ApiService {
// System.out.println("Response n°" + i + ": " + response);
// Thread.sleep(10);
// avancer de 1000 * 15 minutes * 60 pour convertir en secondes et * 1000 pour millisecondes
startTime += (15 * 1000 * 60 * 1000);
// System.out.println(startTime + " " + currEndTime);
startTime += TIME_10_JOURS + 1;
}
this.candles = candles;
}
public String getCandlesJson() throws InterruptedException, com.fasterxml.jackson.core.JsonProcessingException {
public String getCandlesJson(long startDate, long endDate) throws InterruptedException, com.fasterxml.jackson.core.JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
if (this.candles == null || this.candles.isEmpty()) {
this.getCandles();
}
if (this.candlesJson == null) {
this.candlesJson = objectMapper.writeValueAsString(this.candles);
}
// if (this.candles == null || this.candles.isEmpty()) {
// this.getCandles(startDate, endDate);
// }
// if (this.candlesJson == null) {
// this.candlesJson = objectMapper.writeValueAsString(this.candles);
// }
this.getCandles(startDate, endDate);
this.candlesJson = objectMapper.writeValueAsString(this.candles);
return this.candlesJson;
}
// private final WebClient webClient;
......
package com.example.testapi.service;
public class SimulateurService {
private Ticker ticker;
private int periodApproximationSec;
public SimulateurService(int periodApproximationSec) {
this.periodApproximationSec = periodApproximationSec;
// this.ticker = new Ticker(this.);
}
public void pricesApproximations() {
ticker.start(this.periodApproximationSec);
ticker.stop();
}
}
package com.example.testapi.service;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Ticker {
private final Runnable approximationTask;
private final ScheduledExecutorService executorService;
public Ticker(Runnable approximationTask) {
this.approximationTask = approximationTask;
this.executorService = Executors.newScheduledThreadPool(1);
}
public void start(int periodInSec) {
executorService.scheduleAtFixedRate(this.approximationTask, 0, periodInSec, TimeUnit.SECONDS);
}
public void stop() {
executorService.shutdown();
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Graphe des données</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<h1>Test</h1>
<div id="container"></div>
</body>
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.development.js"></script>
<!-- <script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script> -->
<script src="/js/graph.js"></script>
<script src="/js/api.js"></script>
</html>
\ No newline at end of file
......@@ -7,9 +7,15 @@
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<h1>Test</h1>
<div id="container"></div></div>
<h1>Sélectionner les dates de débuts et de fin</h1>
<form>
<label for="dateDebut">Date de début :</label>
<input type="date" id="dateDebut" name="dateDebut"><br>
<label for="dateFin">Date de fin :</label>
<input type="date" id="dateFin" name="dateFin"><br>
<input type="button" value="Afficher le graphique" id="formBtn">
</form>
</body>
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
<script type="module" src="/js/script.js"></script>
<script src="./js/index.script.js"></script>
<script src="./js/api.js"></script>
</html>
\ No newline at end of file
const BASE_URL = "http://localhost:8080/";
const API_URL = BASE_URL + "api";
function callApi(route, method = "GET", data = null)
{
let options = {
method: method,
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
}
};
if (method === "POST" && data) {
options.body = JSON.stringify(data);
}
console.log("fetching data ...");
fetch(API_URL + route, options)
.then(result => result.json())
.then(data => {
switch (route) {
case "/btcusdt":
// console.table(data);
// data.forEach((item, index) => {
// console.log(`Élément ${index}:`, item);
// });
console.log("ok");
displayChart(data);
break;
case "/sendDates":
console.log("Data sent successfully");
window.location.href = BASE_URL + "candlesGraph";
break;
default:
console.log("Unknown route: " + API_URL + route);
break;
}
})
.catch(error => {
console.error("Une erreur est survenue :", error);
});
}
// import { createChart } from './node_modules/lightweight-charts/dist/lightweight-charts.esm.production.js';
const API_URL = "http://localhost:8080/api";
function callApi(route)
{
console.log("okdscas");
fetch(API_URL + route)
.then(result => result.json())
.then(data => {
switch (route) {
case "/btcusdt":
// console.table(data);
// data.forEach((item, index) => {
// console.log(`Élément ${index}:`, item);
// });
displayChart(data);
break;
default:
console.log("Unknown route: " + API_URL + tmp);
break;
}
})
.catch(error => {
console.error("Une erreur est survenue :", error);
});
}
window.onload = function() {
callApi("/btcusdt");
};
function displayChart(data)
{
......@@ -41,15 +19,3 @@ function displayChart(data)
chart.timeScale().fitContent();
}
function transformData(apiData, mapping) {
return apiData.map(item => ({
time: item[mapping.time],
open: item[mapping.open],
high: item[mapping.high],
low: item[mapping.low],
close: item[mapping.close]
}));
}
callApi("/btcusdt");
window.onload = function() {
const dateMax = new Date().toISOString().split("T")[0];
console.log("date max : " + dateMax);
document.getElementById("dateDebut").max = dateMax;
document.getElementById("dateFin").max = dateMax;
const btn = document.getElementById("formBtn");
btn.addEventListener("click", sendDates);
};
function sendDates() {
const dateDebut = document.getElementById("dateDebut").value;
const dateFin = document.getElementById("dateFin").value;
if (!dateDebut || !dateFin) {
alert("Veuillez remplir tous les champs.");
return;
}
const data = {
startDate: dateDebut,
endDate: dateFin
};
callApi("/sendDates", "POST", data);
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment