refactor(oi): improve data extraction and consolidate documentation

- Fix MQL5 API usage in EA to use correct CopyRates and POSITION_TYPE enums
- Refactor scraper data extraction to use drop_duplicates for unique strikes
- Consolidate Windows setup guide into main README
- Add virtual environment batch files for easier setup and execution
- Simplify run_scraper.bat to focus on core execution
- Normalize lot calculation to use SymbolInfo.LotsStep()
This commit is contained in:
Kunthawat Greethong
2026-01-06 20:18:12 +07:00
parent 2e8e07ed17
commit b7c0e68fa8
8 changed files with 386 additions and 895 deletions

View File

@@ -358,64 +358,66 @@ void OnNewM1Bar(datetime newBarTime) {
}
void CalculateVolumeEMAFromHistory() {
double volumeArray[];
ArraySetAsSeries(volumeArray, true);
CopyVolume(_Symbol, PERIOD_M1, 1, InpVolumeEmaPeriod + 1, volumeArray);
double emaAlpha = 2.0 / (InpVolumeEmaPeriod + 1);
double sum = 0;
for(int i = 0; i < InpVolumeEmaPeriod; i++) {
sum += volumeArray[i];
}
double avgVolume = sum / InpVolumeEmaPeriod;
if(VolumeEmaValue == 0) {
VolumeEmaValue = avgVolume;
} else {
VolumeEmaValue = emaAlpha * avgVolume + (1 - emaAlpha) * VolumeEmaValue;
}
MqlRates rates[];
ArraySetAsSeries(rates, true);
int copied = CopyRates(_Symbol, PERIOD_M1, 1, InpVolumeEmaPeriod + 1, rates);
if(copied < InpVolumeEmaPeriod + 1) return;
double emaAlpha = 2.0 / (InpVolumeEmaPeriod + 1);
double sum = 0;
for(int i = 0; i < InpVolumeEmaPeriod; i++) {
sum += (double)rates[i].tick_volume;
}
double avgVolume = sum / InpVolumeEmaPeriod;
if(VolumeEmaValue == 0) {
VolumeEmaValue = avgVolume;
} else {
VolumeEmaValue = emaAlpha * avgVolume + (1 - emaAlpha) * VolumeEmaValue;
}
}
void DetectAbsorptionFromHistory() {
double volumeArray[];
ArraySetAsSeries(volumeArray, true);
CopyVolume(_Symbol, PERIOD_M1, 1, InpAbsorptionBars + 1, volumeArray);
double avgVolume = 0;
for(int i = 0; i < InpAbsorptionBars; i++) {
avgVolume += volumeArray[i];
}
avgVolume /= InpAbsorptionBars;
double volumeThreshold = avgVolume * InpMinVolumeMultiplier;
int sellAbsorptionCount = 0;
int buyAbsorptionCount = 0;
for(int i = 1; i <= InpAbsorptionBars; i++) {
double high = iHigh(_Symbol, PERIOD_M1, i);
double low = iLow(_Symbol, PERIOD_M1, i);
double close = iClose(_Symbol, PERIOD_M1, i);
double open = iOpen(_Symbol, PERIOD_M1, i);
int barVolume = (int)iVolume(_Symbol, PERIOD_M1, i);
double barRange = high - low;
double barDrift = close - open;
bool highVolume = barVolume > volumeThreshold;
bool lowDrift = barRange < InpMaxPriceDriftPoints * _Point;
if(highVolume && lowDrift && barDrift < 0) {
sellAbsorptionCount++;
}
if(highVolume && lowDrift && barDrift > 0) {
buyAbsorptionCount++;
}
}
int requiredBars = MathCeil(InpAbsorptionBars / 2.0);
MqlRates rates[];
ArraySetAsSeries(rates, true);
int copied = CopyRates(_Symbol, PERIOD_M1, 1, InpAbsorptionBars + 1, rates);
if(copied < InpAbsorptionBars + 1) return;
double avgVolume = 0;
for(int i = 0; i < InpAbsorptionBars; i++) {
avgVolume += (double)rates[i].tick_volume;
}
avgVolume /= InpAbsorptionBars;
double volumeThreshold = avgVolume * InpMinVolumeMultiplier;
int sellAbsorptionCount = 0;
int buyAbsorptionCount = 0;
for(int i = 1; i <= InpAbsorptionBars; i++) {
double high = rates[i].high;
double low = rates[i].low;
double close = rates[i].close;
double open = rates[i].open;
int barVolume = (int)rates[i].tick_volume;
double barRange = high - low;
double barDrift = close - open;
bool highVolume = barVolume > volumeThreshold;
bool lowDrift = barRange < InpMaxPriceDriftPoints * _Point;
if(highVolume && lowDrift && barDrift < 0) {
sellAbsorptionCount++;
}
if(highVolume && lowDrift && barDrift > 0) {
buyAbsorptionCount++;
}
}
int requiredBars = (int)MathCeil(InpAbsorptionBars / 2.0);
if(sellAbsorptionCount >= requiredBars && IsPriceNearPutStrike()) {
CurrentAbsorptionState = ABSORPTION_SELL;
@@ -679,8 +681,8 @@ bool IsAtSupport() {
}
void ExecuteBuyTrade() {
double lotSize = CalculateLotSize(ORDER_TYPE_BUY);
if(lotSize <= 0) return;
double lotSize = CalculateLotSize(POSITION_TYPE_BUY);
if(lotSize <= 0) return;
double sl = 0, tp = 0;
double nearestPutStrike = GetNearestPutStrike();
@@ -715,8 +717,8 @@ void ExecuteBuyTrade() {
}
void ExecuteSellTrade() {
double lotSize = CalculateLotSize(ORDER_TYPE_SELL);
if(lotSize <= 0) return;
double lotSize = CalculateLotSize(POSITION_TYPE_SELL);
if(lotSize <= 0) return;
double sl = 0, tp = 0;
double nearestCallStrike = GetNearestCallStrike();
@@ -820,8 +822,8 @@ double CalculateLotSize(ENUM_POSITION_TYPE tradeType) {
}
double NormalizeLot(double lot) {
double step = SymbolInfo.LotStep();
return MathFloor(lot / step) * step;
double step = SymbolInfo.LotsStep();
return MathFloor(lot / step) * step;
}
void ManagePositions() {
@@ -1037,8 +1039,11 @@ void CreateDashboard() {
}
void UpdateDashboard() {
UpdateLabel("DB_Symbol", 20, 45, "Symbol: " + _Symbol, clrWhite, 8);
UpdateLabel("DB_Price", 20, 65, "Price: " + DoubleToString(SpotPrice, 2), clrCyan, 8);
MqlRates rates[];
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 1, rates);
UpdateLabel("DB_Symbol", 20, 45, "Symbol: " + _Symbol, clrWhite, 8);
UpdateLabel("DB_Price", 20, 65, "Price: " + DoubleToString(SpotPrice, 2), clrCyan, 8);
string deltaText = DoubleToString(OrderFlowDeltaPercent, 1) + "%";
color deltaColor = OrderFlowDeltaPercent > 0 ? clrLime : (OrderFlowDeltaPercent < 0 ? clrRed : clrGray);
@@ -1063,9 +1068,9 @@ void UpdateDashboard() {
UpdateLabel("DB_Absorption", 20, 105, "Absorption: " + absorptionText, absorptionColor, 8);
int currentVol = CurrentBarVolume > 0 ? CurrentBarVolume : (int)Volume(0);
UpdateLabel("DB_Volume", 20, 125, "Volume: " + IntegerToString(currentVol) +
" (Avg: " + IntegerToString((int)VolumeEmaValue) + ")", clrWhite, 8);
int currentVol = CurrentBarVolume > 0 ? CurrentBarVolume : (copied > 0 ? (int)rates[0].tick_volume : 0);
UpdateLabel("DB_Volume", 20, 125, "Volume: " + IntegerToString(currentVol) +
" (Avg: " + IntegerToString((int)VolumeEmaValue) + ")", clrWhite, 8);
string driftText = DoubleToString(PriceDrift / _Point, 1) + " pts";
color driftColor = PriceDrift < InpMaxPriceDriftPoints * _Point ? clrLime : clrOrange;