feat(oi): improve csv loading with caching
Add price caching to prevent repeated file reads and improve performance. Implement multi-path search for CSV files with fallback options. Add comprehensive logging for CSV load success/failure states. Update dashboard to display CSV loading status. Simplify scraper CSV output format and automate file transfer to terminal MQL5 Files directory.
This commit is contained in:
Binary file not shown.
@@ -194,6 +194,10 @@ double CurrentBarHigh = 0.0;
|
|||||||
double CurrentBarLow = 0.0;
|
double CurrentBarLow = 0.0;
|
||||||
double LastPrice = 0.0;
|
double LastPrice = 0.0;
|
||||||
|
|
||||||
|
double CachedFuturePrice = -1; // -1 = not loaded, 0 = loaded but failed, >0 = success
|
||||||
|
string LoadedCSVPath = ""; // Path from which CSV was successfully loaded
|
||||||
|
bool CSVLoadLogged = false; // Track if we've logged the result
|
||||||
|
|
||||||
int OnInit() {
|
int OnInit() {
|
||||||
Trade.SetExpertMagicNumber(InpMagicNumber);
|
Trade.SetExpertMagicNumber(InpMagicNumber);
|
||||||
Trade.SetDeviationInPoints(InpMaxSlippage);
|
Trade.SetDeviationInPoints(InpMaxSlippage);
|
||||||
@@ -432,16 +436,7 @@ void UpdateMarketData() {
|
|||||||
SpotPrice = SymbolInfo.Bid();
|
SpotPrice = SymbolInfo.Bid();
|
||||||
SymbolInfo.RefreshRates();
|
SymbolInfo.RefreshRates();
|
||||||
|
|
||||||
double csvFuturePrice = LoadFuturePriceFromCSV();
|
FuturePrice = LoadFuturePriceFromCSV();
|
||||||
if(csvFuturePrice > 0) {
|
|
||||||
FuturePrice = csvFuturePrice;
|
|
||||||
} else if(DynamicFuturePrice > 0) {
|
|
||||||
FuturePrice = DynamicFuturePrice;
|
|
||||||
} else if(InpManualFuturePrice > 0) {
|
|
||||||
FuturePrice = InpManualFuturePrice;
|
|
||||||
} else {
|
|
||||||
FuturePrice = SpotPrice;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPriceNearPutStrike() {
|
bool IsPriceNearPutStrike() {
|
||||||
@@ -485,6 +480,13 @@ bool IsInMiddleOfRange() {
|
|||||||
bool CheckGlobalConditions() {
|
bool CheckGlobalConditions() {
|
||||||
if(!TradingEnabled) return false;
|
if(!TradingEnabled) return false;
|
||||||
|
|
||||||
|
if(CachedFuturePrice < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(CachedFuturePrice == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return false;
|
if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return false;
|
||||||
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_ALLOWED)) return false;
|
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_ALLOWED)) return false;
|
||||||
|
|
||||||
@@ -983,33 +985,83 @@ bool InitializeIndicators() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double LoadFuturePriceFromCSV() {
|
double LoadFuturePriceFromCSV() {
|
||||||
string path = InpOICsvPath;
|
if(CachedFuturePrice >= 0) {
|
||||||
int filehandle = FileOpen(path, FILE_READ | FILE_CSV, ',');
|
return CachedFuturePrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
string paths[];
|
||||||
|
int pathCount = 0;
|
||||||
|
|
||||||
|
ArrayResize(paths, 5);
|
||||||
|
paths[pathCount++] = InpOICsvPath;
|
||||||
|
paths[pathCount++] = "oi_data.csv";
|
||||||
|
paths[pathCount++] = "\\Files\\oi_data.csv";
|
||||||
|
paths[pathCount++] = "..\\oi_scraper\\oi_data.csv";
|
||||||
|
paths[pathCount++] = "../oi_scraper/oi_data.csv";
|
||||||
|
|
||||||
|
int filehandle = INVALID_HANDLE;
|
||||||
|
string foundPath = "";
|
||||||
|
|
||||||
|
for(int i = 0; i < pathCount; i++) {
|
||||||
|
filehandle = FileOpen(paths[i], FILE_READ | FILE_CSV, ',');
|
||||||
|
if(filehandle != INVALID_HANDLE) {
|
||||||
|
foundPath = paths[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(filehandle == INVALID_HANDLE) {
|
if(filehandle == INVALID_HANDLE) {
|
||||||
|
if(!CSVLoadLogged) {
|
||||||
|
Print("CSV ERROR: File not found. Searched paths:");
|
||||||
|
for(int i = 0; i < pathCount; i++) {
|
||||||
|
Print(" - ", paths[i]);
|
||||||
|
}
|
||||||
|
CSVLoadLogged = true;
|
||||||
|
}
|
||||||
|
CachedFuturePrice = 0;
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double futurePrice = 0.0;
|
double futurePrice = 0.0;
|
||||||
|
int dataLineCount = 0;
|
||||||
|
|
||||||
while(!FileIsEnding(filehandle)) {
|
while(!FileIsEnding(filehandle)) {
|
||||||
string line = FileReadString(filehandle);
|
string line = FileReadString(filehandle);
|
||||||
|
dataLineCount++;
|
||||||
|
|
||||||
|
if(line == "") continue;
|
||||||
|
|
||||||
string parts[];
|
string parts[];
|
||||||
int split = StringSplit(line, ',', parts);
|
int split = StringSplit(line, ',', parts);
|
||||||
|
|
||||||
if(split >= 2) {
|
if(split >= 2) {
|
||||||
string dateStr = parts[0];
|
double price = StringToDouble(parts[1]);
|
||||||
double future = StringToDouble(parts[1]);
|
if(price > 0) {
|
||||||
|
futurePrice = price;
|
||||||
if(future > 0) {
|
|
||||||
futurePrice = future;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileClose(filehandle);
|
FileClose(filehandle);
|
||||||
return futurePrice;
|
|
||||||
|
if(futurePrice > 0) {
|
||||||
|
CachedFuturePrice = futurePrice;
|
||||||
|
LoadedCSVPath = foundPath;
|
||||||
|
if(!CSVLoadLogged) {
|
||||||
|
Print("CSV SUCCESS: FuturePrice=", futurePrice, " loaded from ", foundPath);
|
||||||
|
CSVLoadLogged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!CSVLoadLogged) {
|
||||||
|
Print("CSV ERROR: No valid price found in ", foundPath);
|
||||||
|
Print(" - File exists but contains no parseable price data");
|
||||||
|
CSVLoadLogged = true;
|
||||||
|
}
|
||||||
|
CachedFuturePrice = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CachedFuturePrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckExistingPositions() {
|
void CheckExistingPositions() {
|
||||||
@@ -1028,74 +1080,88 @@ void CheckExistingPositions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CreateDashboard() {
|
void CreateDashboard() {
|
||||||
int panelWidth = 280;
|
int panelWidth = 280;
|
||||||
int panelHeight = 280;
|
int panelHeight = 300;
|
||||||
|
|
||||||
CreatePanel("DashboardPanel", 10, 10, panelWidth, panelHeight, C'25,25,35', BORDER_FLAT);
|
CreatePanel("DashboardPanel", 10, 10, panelWidth, panelHeight, C'25,25,35', BORDER_FLAT);
|
||||||
|
|
||||||
CreateLabel("DB_Title", 20, 20, "ORDER FLOW ABSORPTION EA", clrYellow, 10);
|
CreateLabel("DB_Title", 20, 20, "ORDER FLOW ABSORPTION EA", clrYellow, 10);
|
||||||
|
|
||||||
UpdateDashboard();
|
UpdateDashboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateDashboard() {
|
void UpdateDashboard() {
|
||||||
MqlRates rates[];
|
MqlRates rates[];
|
||||||
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 1, rates);
|
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 1, rates);
|
||||||
|
|
||||||
UpdateLabel("DB_Symbol", 20, 45, "Symbol: " + _Symbol, clrWhite, 8);
|
UpdateLabel("DB_Symbol", 20, 45, "Symbol: " + _Symbol, clrWhite, 8);
|
||||||
UpdateLabel("DB_Price", 20, 65, "Price: " + DoubleToString(SpotPrice, 2), clrCyan, 8);
|
UpdateLabel("DB_Price", 20, 65, "Price: " + DoubleToString(SpotPrice, 2), clrCyan, 8);
|
||||||
|
|
||||||
string deltaText = DoubleToString(OrderFlowDeltaPercent, 1) + "%";
|
string deltaText = DoubleToString(OrderFlowDeltaPercent, 1) + "%";
|
||||||
color deltaColor = OrderFlowDeltaPercent > 0 ? clrLime : (OrderFlowDeltaPercent < 0 ? clrRed : clrGray);
|
color deltaColor = OrderFlowDeltaPercent > 0 ? clrLime : (OrderFlowDeltaPercent < 0 ? clrRed : clrGray);
|
||||||
UpdateLabel("DB_Delta", 20, 85, "Delta: " + deltaText, deltaColor, 8);
|
UpdateLabel("DB_Delta", 20, 85, "Delta: " + deltaText, deltaColor, 8);
|
||||||
|
|
||||||
string absorptionText = "";
|
string absorptionText = "";
|
||||||
color absorptionColor = clrGray;
|
color absorptionColor = clrGray;
|
||||||
|
|
||||||
switch(CurrentAbsorptionState) {
|
switch(CurrentAbsorptionState) {
|
||||||
case ABSORPTION_BUY:
|
case ABSORPTION_BUY:
|
||||||
absorptionText = "BUY ABSORPTION";
|
absorptionText = "BUY ABSORPTION";
|
||||||
absorptionColor = clrLime;
|
absorptionColor = clrLime;
|
||||||
break;
|
break;
|
||||||
case ABSORPTION_SELL:
|
case ABSORPTION_SELL:
|
||||||
absorptionText = "SELL ABSORPTION";
|
absorptionText = "SELL ABSORPTION";
|
||||||
absorptionColor = clrRed;
|
absorptionColor = clrRed;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
absorptionText = "NONE";
|
absorptionText = "NONE";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateLabel("DB_Absorption", 20, 105, "Absorption: " + absorptionText, absorptionColor, 8);
|
UpdateLabel("DB_Absorption", 20, 105, "Absorption: " + absorptionText, absorptionColor, 8);
|
||||||
|
|
||||||
int currentVol = CurrentBarVolume > 0 ? CurrentBarVolume : (copied > 0 ? (int)rates[0].tick_volume : 0);
|
int currentVol = CurrentBarVolume > 0 ? CurrentBarVolume : (copied > 0 ? (int)rates[0].tick_volume : 0);
|
||||||
UpdateLabel("DB_Volume", 20, 125, "Volume: " + IntegerToString(currentVol) +
|
UpdateLabel("DB_Volume", 20, 125, "Volume: " + IntegerToString(currentVol) +
|
||||||
" (Avg: " + IntegerToString((int)VolumeEmaValue) + ")", clrWhite, 8);
|
" (Avg: " + IntegerToString((int)VolumeEmaValue) + ")", clrWhite, 8);
|
||||||
|
|
||||||
string driftText = DoubleToString(PriceDrift / _Point, 1) + " pts";
|
string driftText = DoubleToString(PriceDrift / _Point, 1) + " pts";
|
||||||
color driftColor = PriceDrift < InpMaxPriceDriftPoints * _Point ? clrLime : clrOrange;
|
color driftColor = PriceDrift < InpMaxPriceDriftPoints * _Point ? clrLime : clrOrange;
|
||||||
UpdateLabel("DB_PriceDrift", 20, 145, "Drift: " + driftText, driftColor, 8);
|
UpdateLabel("DB_PriceDrift", 20, 145, "Drift: " + driftText, driftColor, 8);
|
||||||
|
|
||||||
UpdateLabel("DB_Trades", 20, 170, "Daily: " + IntegerToString(DailyTradeCount) +
|
string csvText = "";
|
||||||
"/" + IntegerToString(InpMaxDailyTrades), clrWhite, 8);
|
color csvColor = clrGray;
|
||||||
|
if(CachedFuturePrice < 0) {
|
||||||
|
csvText = "CSV: LOADING...";
|
||||||
|
csvColor = clrYellow;
|
||||||
|
} else if(CachedFuturePrice > 0) {
|
||||||
|
csvText = "CSV: OK (" + DoubleToString(CachedFuturePrice, 2) + ")";
|
||||||
|
csvColor = clrLime;
|
||||||
|
} else {
|
||||||
|
csvText = "CSV: FAILED";
|
||||||
|
csvColor = clrRed;
|
||||||
|
}
|
||||||
|
UpdateLabel("DB_CSVStatus", 20, 160, csvText, csvColor, 8);
|
||||||
|
|
||||||
UpdateLabel("DB_PnL", 20, 190, "Daily PnL: " + DoubleToString(DailyPnL, 2),
|
UpdateLabel("DB_Trades", 20, 180, "Daily: " + IntegerToString(DailyTradeCount) +
|
||||||
DailyPnL >= 0 ? clrLime : clrRed, 8);
|
"/" + IntegerToString(InpMaxDailyTrades), clrWhite, 8);
|
||||||
|
|
||||||
string tradingStatus = TradingEnabled ? "ENABLED" : "DISABLED";
|
UpdateLabel("DB_PnL", 20, 200, "Daily PnL: " + DoubleToString(DailyPnL, 2),
|
||||||
color statusColor = TradingEnabled ? clrLime : clrRed;
|
DailyPnL >= 0 ? clrLime : clrRed, 8);
|
||||||
UpdateLabel("DB_Status", 20, 215, "Trading: " + tradingStatus, statusColor, 8);
|
|
||||||
|
|
||||||
string nearZone = "";
|
string tradingStatus = TradingEnabled ? "ENABLED" : "DISABLED";
|
||||||
if(IsPriceNearPutStrike()) nearZone = "NEAR PUT";
|
color statusColor = TradingEnabled ? clrLime : clrRed;
|
||||||
else if(IsPriceNearCallStrike()) nearZone = "NEAR CALL";
|
UpdateLabel("DB_Status", 20, 220, "Trading: " + tradingStatus, statusColor, 8);
|
||||||
else nearZone = "MIDDLE";
|
|
||||||
|
|
||||||
color zoneColor = (nearZone == "MIDDLE") ? clrOrange : clrCyan;
|
string nearZone = "";
|
||||||
UpdateLabel("DB_Zone", 20, 235, "Zone: " + nearZone, zoneColor, 8);
|
if(IsPriceNearPutStrike()) nearZone = "NEAR PUT";
|
||||||
|
else if(IsPriceNearCallStrike()) nearZone = "NEAR CALL";
|
||||||
|
else nearZone = "MIDDLE";
|
||||||
|
|
||||||
string ticksText = "Ticks: " + IntegerToString(CurrentBarUpTicks) + "/" + IntegerToString(CurrentBarDownTicks);
|
color zoneColor = (nearZone == "MIDDLE") ? clrOrange : clrCyan;
|
||||||
UpdateLabel("DB_Ticks", 20, 255, ticksText, clrWhite, 8);
|
UpdateLabel("DB_Zone", 20, 240, "Zone: " + nearZone, zoneColor, 8);
|
||||||
|
|
||||||
|
string ticksText = "Ticks: " + IntegerToString(CurrentBarUpTicks) + "/" + IntegerToString(CurrentBarDownTicks);
|
||||||
|
UpdateLabel("DB_Ticks", 20, 260, ticksText, clrWhite, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateControlPanel() {
|
void CreateControlPanel() {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Requires: pip install -r requirements.txt
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
from datetime import datetime
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -290,9 +291,8 @@ def export_to_csv(df, future_price=0.0):
|
|||||||
output_path = CSV_OUTPUT_PATH
|
output_path = CSV_OUTPUT_PATH
|
||||||
|
|
||||||
with open(output_path, "w") as f:
|
with open(output_path, "w") as f:
|
||||||
df.to_csv(f, index=False)
|
f.write("date,future_price\n")
|
||||||
f.write("\n[Price]\n")
|
f.write(f"{datetime.now().strftime('%Y-%m-%d')},{future_price}\n")
|
||||||
f.write(f"FuturePrice,{future_price}\n")
|
|
||||||
|
|
||||||
logger.info(f"Exported OI data and price to {output_path}")
|
logger.info(f"Exported OI data and price to {output_path}")
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ call venv\Scripts\activate.bat
|
|||||||
REM Run Python scraper
|
REM Run Python scraper
|
||||||
python main.py
|
python main.py
|
||||||
|
|
||||||
|
MOVE "oi_data.csv" "C:\Users\limitrack\AppData\Roaming\MetaQuotes\Terminal\53785E099C927DB68A545C249CDBCE06\MQL5\Files\"
|
||||||
|
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo Scraper completed. Check oi_data.csv for results.
|
echo Scraper completed. Check oi_data.csv for results.
|
||||||
timeout /t 5
|
timeout /t 5
|
||||||
|
|||||||
Reference in New Issue
Block a user