|
@@ -35,8 +35,8 @@ int OnInit()
|
|
|
|
|
|
|
|
Print("✅ Symbols initialized: ", ArraySize(symbols));
|
|
Print("✅ Symbols initialized: ", ArraySize(symbols));
|
|
|
SendAllHistoricalCandles();
|
|
SendAllHistoricalCandles();
|
|
|
- EventSetTimer(60); // ⏱️ Trigger OnTimer() every 30 minutes
|
|
|
|
|
- Print("✅ Timer set: SendAllHistoricalCandles() will run every 30 minutes.");
|
|
|
|
|
|
|
+ EventSetTimer(60); // ⏱️ Trigger OnTimer() every 60 seconds
|
|
|
|
|
+ Print("✅ Timer set: SendAllHistoricalCandles() will run every 10 minutes (checked every 60 seconds).");
|
|
|
return(INIT_SUCCEEDED);
|
|
return(INIT_SUCCEEDED);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -253,7 +253,7 @@ int CreateSymbolInDatabase(string symbolName)
|
|
|
if(StringFind(symbolName, "BTC") == 0 || StringFind(symbolName, "ETH") == 0)
|
|
if(StringFind(symbolName, "BTC") == 0 || StringFind(symbolName, "ETH") == 0)
|
|
|
instrumentType = "crypto";
|
|
instrumentType = "crypto";
|
|
|
else if(StringFind(symbolName, "XAU") == 0 || StringFind(symbolName, "XAG") == 0)
|
|
else if(StringFind(symbolName, "XAU") == 0 || StringFind(symbolName, "XAG") == 0)
|
|
|
- instrumentType = "metal";
|
|
|
|
|
|
|
+ instrumentType = "commodity"; // ✅ Fixed: "metal" not in API validation, use "commodity"
|
|
|
else if(StringFind(symbolName, "US30") == 0 || StringFind(symbolName, "NAS") == 0)
|
|
else if(StringFind(symbolName, "US30") == 0 || StringFind(symbolName, "NAS") == 0)
|
|
|
instrumentType = "index";
|
|
instrumentType = "index";
|
|
|
else
|
|
else
|
|
@@ -611,7 +611,7 @@ string BuildCandleJSONFromRates(int symbolId, MqlRates &rates[], int startIndex,
|
|
|
double quoteVolume = (r.real_volume > 0 ? r.real_volume : volume);
|
|
double quoteVolume = (r.real_volume > 0 ? r.real_volume : volume);
|
|
|
|
|
|
|
|
string one = StringFormat(
|
|
string one = StringFormat(
|
|
|
- "{\"symbolId\":%d,\"timeframe\":\"%s\",\"openTime\":\"%s\",\"closeTime\":\"%s\",\"open\":%.5f,\"high\":%.5f,\"low\":%.5f,\"close\":%.5f,\"volume\":%.5f,\"tradesCount\":%d,\"quoteVolume\":%.5f}",
|
|
|
|
|
|
|
+ "{\"symbolId\":%d,\"timeframe\":\"%s\",\"openTime\":\"%s\",\"closeTime\":\"%s\",\"open\":%.8f,\"high\":%.8f,\"low\":%.8f,\"close\":%.8f,\"volume\":%.8f,\"tradesCount\":%d,\"quoteVolume\":%.8f}",
|
|
|
symbolId, timeframe, openTime, closeTime,
|
|
symbolId, timeframe, openTime, closeTime,
|
|
|
r.open, r.high, r.low, r.close,
|
|
r.open, r.high, r.low, r.close,
|
|
|
volume, (int)volume, quoteVolume
|
|
volume, (int)volume, quoteVolume
|
|
@@ -668,11 +668,11 @@ bool SendJSON(string url, string json, string &response)
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
|
-//| Cleanup old candles (keep only last 1000) |
|
|
|
|
|
|
|
+//| Cleanup old candles (keep only last 1000) for a specific timeframe |
|
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
|
-void CleanupOldCandles(int symbolId)
|
|
|
|
|
|
|
+void CleanupOldCandles(int symbolId, string timeframe)
|
|
|
{
|
|
{
|
|
|
- string url = ApiBaseUrl + "/api/candles/cleanup/" + IntegerToString(symbolId) + "?keep=1000";
|
|
|
|
|
|
|
+ string url = ApiBaseUrl + "/api/candles/cleanup/" + IntegerToString(symbolId) + "?timeframe=" + timeframe + "&keep=1000";
|
|
|
string headers = "Content-Type: application/json\r\n";
|
|
string headers = "Content-Type: application/json\r\n";
|
|
|
string resultHeaders = "";
|
|
string resultHeaders = "";
|
|
|
char result[];
|
|
char result[];
|
|
@@ -683,9 +683,9 @@ void CleanupOldCandles(int symbolId)
|
|
|
|
|
|
|
|
string response = CharArrayToString(result);
|
|
string response = CharArrayToString(result);
|
|
|
if(res == 200 || res == 204)
|
|
if(res == 200 || res == 204)
|
|
|
- Print("🧹 Cleanup successful for symbolId=", symbolId, " → kept last 1000 candles.");
|
|
|
|
|
|
|
+ Print("🧹 Cleanup successful for symbolId=", symbolId, " timeframe=", timeframe, " → kept last 1000 candles.");
|
|
|
else
|
|
else
|
|
|
- Print("⚠️ Cleanup failed for symbolId=", symbolId, " HTTP=", res, " Response=", response);
|
|
|
|
|
|
|
+ Print("⚠️ Cleanup failed for symbolId=", symbolId, " timeframe=", timeframe, " HTTP=", res, " Response=", response);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
@@ -695,19 +695,25 @@ void OnTimer()
|
|
|
{
|
|
{
|
|
|
datetime now = TimeCurrent();
|
|
datetime now = TimeCurrent();
|
|
|
|
|
|
|
|
- // ✅ Run full candle sync only once every minute
|
|
|
|
|
|
|
+ // ✅ Run full candle sync only once every 10 minutes (600 seconds)
|
|
|
if(now - lastCandleSync >= 600)
|
|
if(now - lastCandleSync >= 600)
|
|
|
{
|
|
{
|
|
|
Print("⏰ Running scheduled candle sync and cleanup...");
|
|
Print("⏰ Running scheduled candle sync and cleanup...");
|
|
|
SendAllHistoricalCandles();
|
|
SendAllHistoricalCandles();
|
|
|
|
|
|
|
|
- // ✅ After uploading candles, clean up old ones
|
|
|
|
|
|
|
+ // ✅ After uploading candles, clean up old ones for all timeframes
|
|
|
for(int i = 0; i < ArraySize(symbols); i++)
|
|
for(int i = 0; i < ArraySize(symbols); i++)
|
|
|
{
|
|
{
|
|
|
int symId = symbolIds[i];
|
|
int symId = symbolIds[i];
|
|
|
if(symId <= 0) continue;
|
|
if(symId <= 0) continue;
|
|
|
- CleanupOldCandles(symId);
|
|
|
|
|
- Sleep(500); // small delay to avoid API overload
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Clean up for each timeframe
|
|
|
|
|
+ for(int tfIndex = 0; tfIndex < ArraySize(Timeframes); tfIndex++)
|
|
|
|
|
+ {
|
|
|
|
|
+ string tfStr = TimeframeStrings[tfIndex];
|
|
|
|
|
+ CleanupOldCandles(symId, tfStr);
|
|
|
|
|
+ Sleep(300); // small delay to avoid API overload
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
lastCandleSync = now;
|
|
lastCandleSync = now;
|