1381 lines
42 KiB
Plaintext
1381 lines
42 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| OI_OrderFlow_Absorption_XAUUSD.mq5 |
|
|
//| Order Flow Absorption Mean Reversion System |
|
|
//| Version 2.0 |
|
|
//+------------------------------------------------------------------+
|
|
|
|
#property copyright "Order Flow Absorption Mean Reversion System"
|
|
#property link ""
|
|
#property version "2.00"
|
|
#property description "XAUUSD Mean Reversion with OI Levels & Order Flow Absorption"
|
|
#property strict
|
|
|
|
#include <Trade\Trade.mqh>
|
|
#include <Trade\AccountInfo.mqh>
|
|
#include <Trade\SymbolInfo.mqh>
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include <Trade\OrderInfo.mqh>
|
|
#include <Trade\HistoryOrderInfo.mqh>
|
|
#include <Arrays\ArrayObj.mqh>
|
|
#include <Arrays\ArrayDouble.mqh>
|
|
|
|
enum ENUM_MARKET_PHASE {
|
|
PHASE_BULLISH,
|
|
PHASE_BEARISH,
|
|
PHASE_SIDEWAYS,
|
|
PHASE_NEUTRAL
|
|
};
|
|
|
|
enum ENUM_ABSORPTION_STATE {
|
|
ABSORPTION_NONE,
|
|
ABSORPTION_BUY,
|
|
ABSORPTION_SELL
|
|
};
|
|
|
|
enum ENUM_OI_SOURCE {
|
|
OI_SOURCE_MANUAL,
|
|
OI_SOURCE_CSV_FILE,
|
|
OI_SOURCE_AUTO_SYNC
|
|
};
|
|
|
|
input group "=== OI & DELTA SETTINGS ==="
|
|
input ENUM_OI_SOURCE InpOISource = OI_SOURCE_CSV_FILE;
|
|
input string InpOICsvPath = "oi_data.csv";
|
|
input double InpManualFuturePrice = 0.0;
|
|
|
|
input double InpCallStrike1 = 0.0;
|
|
input double InpCallStrike2 = 0.0;
|
|
input double InpCallStrike3 = 0.0;
|
|
input double InpPutStrike1 = 0.0;
|
|
input double InpPutStrike2 = 0.0;
|
|
input double InpPutStrike3 = 0.0;
|
|
|
|
input group "=== ORDER FLOW & ABSORPTION ==="
|
|
input bool InpUseAbsorptionFilter = true;
|
|
input int InpAbsorptionBars = 3;
|
|
input int InpOIZonePoints = 100;
|
|
input double InpMinVolumeMultiplier = 1.5;
|
|
input int InpVolumeEmaPeriod = 20;
|
|
input int InpMaxPriceDriftPoints = 50;
|
|
input int InpSLBufferPoints = 200;
|
|
|
|
input group "=== TRADING SETTINGS ==="
|
|
input double InpLotSize = 0.1;
|
|
input bool InpUseMoneyManagement = true;
|
|
input double InpRiskPercent = 1.0;
|
|
input bool InpUseStopLoss = true;
|
|
input bool InpUseTakeProfit = true;
|
|
input int InpStopLossPoints = 300;
|
|
input int InpTakeProfitPoints = 500;
|
|
input int InpMaxSpread = 200;
|
|
input int InpMaxSlippage = 10;
|
|
input int InpMagicNumber = 202501;
|
|
|
|
input group "=== MARKET FILTERS ==="
|
|
input bool InpUseMarketPhaseFilter = true;
|
|
input ENUM_TIMEFRAMES InpTrendTF = PERIOD_H4;
|
|
input int InpTrendMAPeriod1 = 50;
|
|
input int InpTrendMAPeriod2 = 200;
|
|
input bool InpUseATRFilter = true;
|
|
input double InpMaxATRPercent = 2.0;
|
|
input bool InpUseSessionFilter = true;
|
|
input int InpSessionStartHour = 8;
|
|
input int InpSessionEndHour = 22;
|
|
|
|
input group "=== RISK MANAGEMENT ==="
|
|
input double InpMaxDailyLossPercent = 3.0;
|
|
input double InpMaxDailyProfitPercent = 5.0;
|
|
input int InpMaxConsecutiveLosses = 3;
|
|
input bool InpDisableAfterMaxLoss = true;
|
|
input double InpEquityProtectionPercent = 10.0;
|
|
input bool InpCloseAllOnReverse = false;
|
|
input int InpMaxDailyTrades = 10;
|
|
|
|
input group "=== ADVANCED FEATURES ==="
|
|
input bool InpUseTrailingStop = false;
|
|
input int InpTrailingStartPoints = 150;
|
|
input int InpTrailingStepPoints = 50;
|
|
input bool InpUseSoftStopLoss = true;
|
|
input bool InpUseAutoRecovery = true;
|
|
input bool InpEnableDashboard = true;
|
|
input int InpCSVReloadInterval = 60;
|
|
|
|
CTrade Trade;
|
|
CSymbolInfo SymbolInfo;
|
|
CAccountInfo AccountInfo;
|
|
CPositionInfo PositionInfo;
|
|
COrderInfo OrderInfo;
|
|
CHistoryOrderInfo HistoryOrderInfo;
|
|
|
|
double FuturePrice = 0.0;
|
|
double SpotPrice = 0.0;
|
|
|
|
double CallLevels[3];
|
|
double PutLevels[3];
|
|
int CallOI[3];
|
|
int PutOI[3];
|
|
|
|
double DynamicFuturePrice = 0.0;
|
|
double DynamicCallStrike1 = 0.0;
|
|
double DynamicCallStrike2 = 0.0;
|
|
double DynamicCallStrike3 = 0.0;
|
|
double DynamicPutStrike1 = 0.0;
|
|
double DynamicPutStrike2 = 0.0;
|
|
double DynamicPutStrike3 = 0.0;
|
|
|
|
double LevelUpper1 = 0.0;
|
|
double LevelUpper2 = 0.0;
|
|
double LevelMid = 0.0;
|
|
double LevelLower1 = 0.0;
|
|
double LevelLower2 = 0.0;
|
|
|
|
int DailyTradeCount = 0;
|
|
double DailyPnL = 0.0;
|
|
double DailyProfit = 0.0;
|
|
double DailyLoss = 0.0;
|
|
int ConsecutiveLosses = 0;
|
|
datetime LastTradeTime = 0;
|
|
datetime LastResetDate = 0;
|
|
|
|
bool TradingEnabled = true;
|
|
double EquityHigh = 0.0;
|
|
double EquityLow = 0.0;
|
|
|
|
int ATRHandle = INVALID_HANDLE;
|
|
int MAFastHandle = INVALID_HANDLE;
|
|
int MASlowHandle = INVALID_HANDLE;
|
|
int RSIMainHandle = INVALID_HANDLE;
|
|
|
|
long chart_id = 0;
|
|
int DashboardSubWindow = -1;
|
|
color PanelColor = C'30,30,30';
|
|
color TextColor = clrWhite;
|
|
color ProfitColor = clrLime;
|
|
color LossColor = clrRed;
|
|
color WarningColor = clrOrange;
|
|
|
|
int ControlPanelX = 10;
|
|
int ControlPanelY = 210;
|
|
|
|
double ManualFuturePriceValue = 0.0;
|
|
double CallStrike1Value = 0.0;
|
|
double CallStrike2Value = 0.0;
|
|
double CallStrike3Value = 0.0;
|
|
double PutStrike1Value = 0.0;
|
|
double PutStrike2Value = 0.0;
|
|
double PutStrike3Value = 0.0;
|
|
|
|
string ControlPanelObjects[] = {
|
|
"CP_TopPanel",
|
|
"CP_CloseAll",
|
|
"CP_OIDataPanel",
|
|
"CP_FuturePrice",
|
|
"CP_CallStrike1",
|
|
"CP_CallStrike2",
|
|
"CP_CallStrike3",
|
|
"CP_PutStrike1",
|
|
"CP_PutStrike2",
|
|
"CP_PutStrike3",
|
|
"CP_UpdateOI"
|
|
};
|
|
|
|
string DrawnLines[];
|
|
int MaxLines = 10;
|
|
|
|
double OrderFlowDeltaPercent = 0.0;
|
|
double VolumeEmaValue = 0.0;
|
|
double PriceDrift = 0.0;
|
|
ENUM_ABSORPTION_STATE CurrentAbsorptionState = ABSORPTION_NONE;
|
|
|
|
datetime LastM1BarTime = 0;
|
|
datetime LastDashboardUpdate = 0;
|
|
|
|
int CurrentBarUpTicks = 0;
|
|
int CurrentBarDownTicks = 0;
|
|
int CurrentBarVolume = 0;
|
|
double CurrentBarHigh = 0.0;
|
|
double CurrentBarLow = 0.0;
|
|
double LastPrice = 0.0;
|
|
|
|
double CachedFuturePrice = -1;
|
|
string LoadedCSVPath = "";
|
|
bool CSVLoadLogged = false;
|
|
datetime LastCSVReloadTime = 0;
|
|
|
|
int OnInit() {
|
|
Trade.SetExpertMagicNumber(InpMagicNumber);
|
|
Trade.SetDeviationInPoints(InpMaxSlippage);
|
|
Trade.SetTypeFilling(ORDER_FILLING_IOC);
|
|
|
|
SymbolInfo.Name(_Symbol);
|
|
SymbolInfo.RefreshRates();
|
|
|
|
DynamicFuturePrice = InpManualFuturePrice;
|
|
DynamicCallStrike1 = InpCallStrike1;
|
|
DynamicCallStrike2 = InpCallStrike2;
|
|
DynamicCallStrike3 = InpCallStrike3;
|
|
DynamicPutStrike1 = InpPutStrike1;
|
|
DynamicPutStrike2 = InpPutStrike2;
|
|
DynamicPutStrike3 = InpPutStrike3;
|
|
|
|
InitializeOILevels();
|
|
InitializeKeyLevels();
|
|
|
|
if(InpOISource == OI_SOURCE_CSV_FILE) {
|
|
LoadOIFromCSV();
|
|
}
|
|
|
|
if(!InitializeIndicators()) {
|
|
Print("Error initializing indicators");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
EquityHigh = AccountInfo.Equity();
|
|
EquityLow = AccountInfo.Equity();
|
|
|
|
if(InpEnableDashboard) {
|
|
chart_id = ChartID();
|
|
CreateDashboard();
|
|
CreateControlPanel();
|
|
}
|
|
|
|
if(InpUseAutoRecovery) {
|
|
CheckExistingPositions();
|
|
}
|
|
|
|
EventSetTimer(1);
|
|
|
|
ArrayResize(DrawnLines, MaxLines);
|
|
for(int i = 0; i < MaxLines; i++) {
|
|
DrawnLines[i] = "";
|
|
}
|
|
|
|
LastPrice = SymbolInfo.Bid();
|
|
CurrentBarHigh = LastPrice;
|
|
CurrentBarLow = LastPrice;
|
|
LastM1BarTime = iTime(_Symbol, PERIOD_M1, 0);
|
|
|
|
Print("EA Initialized Successfully: Order Flow Absorption Strategy");
|
|
Print("Symbol: ", _Symbol);
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
void OnDeinit(const int reason) {
|
|
if(InpEnableDashboard) {
|
|
ObjectsDeleteAll(chart_id, 0, -1);
|
|
}
|
|
|
|
if(ATRHandle != INVALID_HANDLE) IndicatorRelease(ATRHandle);
|
|
if(MAFastHandle != INVALID_HANDLE) IndicatorRelease(MAFastHandle);
|
|
if(MASlowHandle != INVALID_HANDLE) IndicatorRelease(MASlowHandle);
|
|
if(RSIMainHandle != INVALID_HANDLE) IndicatorRelease(RSIMainHandle);
|
|
|
|
EventKillTimer();
|
|
Print("EA Deinitialized");
|
|
}
|
|
|
|
void OnTick() {
|
|
if(Bars(_Symbol, _Period) < 100)
|
|
return;
|
|
|
|
UpdateMarketData();
|
|
CountTicks();
|
|
UpdatePriceDrift();
|
|
|
|
datetime currentM1BarTime = iTime(_Symbol, PERIOD_M1, 0);
|
|
|
|
if(currentM1BarTime != LastM1BarTime) {
|
|
OnNewM1Bar(currentM1BarTime);
|
|
}
|
|
|
|
if(!CheckGlobalConditions())
|
|
return;
|
|
|
|
CheckTradingSignals();
|
|
ManagePositions();
|
|
|
|
if(InpEnableDashboard && TimeCurrent() - LastDashboardUpdate >= 1) {
|
|
UpdateDashboard();
|
|
UpdateControlPanel();
|
|
LastDashboardUpdate = TimeCurrent();
|
|
}
|
|
}
|
|
|
|
void OnTimer() {
|
|
if(InpEnableDashboard && TimeCurrent() - LastDashboardUpdate >= 1) {
|
|
UpdateDashboard();
|
|
UpdateControlPanel();
|
|
LastDashboardUpdate = TimeCurrent();
|
|
}
|
|
|
|
CheckDailyReset();
|
|
|
|
if(InpOISource == OI_SOURCE_CSV_FILE && InpCSVReloadInterval > 0) {
|
|
if(TimeCurrent() - LastCSVReloadTime >= InpCSVReloadInterval * 60) {
|
|
Print("CSV Reload: Scheduled reload triggered");
|
|
LastCSVReloadTime = TimeCurrent();
|
|
CachedFuturePrice = -1;
|
|
LoadOIFromCSV();
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
|
|
if(id == CHARTEVENT_OBJECT_CLICK) {
|
|
HandleControlPanelClick(sparam);
|
|
}
|
|
|
|
if(id == CHARTEVENT_OBJECT_ENDEDIT) {
|
|
UpdateInputValues();
|
|
Print("Edit field updated: ", sparam);
|
|
}
|
|
}
|
|
|
|
void CountTicks() {
|
|
double currentPrice = SymbolInfo.Bid();
|
|
|
|
if(LastPrice == 0) {
|
|
LastPrice = currentPrice;
|
|
CurrentBarHigh = currentPrice;
|
|
CurrentBarLow = currentPrice;
|
|
return;
|
|
}
|
|
|
|
if(currentPrice > LastPrice) {
|
|
CurrentBarUpTicks++;
|
|
} else if(currentPrice < LastPrice) {
|
|
CurrentBarDownTicks++;
|
|
}
|
|
|
|
CurrentBarVolume++;
|
|
LastPrice = currentPrice;
|
|
}
|
|
|
|
void UpdatePriceDrift() {
|
|
CurrentBarHigh = MathMax(CurrentBarHigh, SpotPrice);
|
|
CurrentBarLow = MathMin(CurrentBarLow, SpotPrice);
|
|
PriceDrift = CurrentBarHigh - CurrentBarLow;
|
|
}
|
|
|
|
void OnNewM1Bar(datetime newBarTime) {
|
|
int totalTicks = CurrentBarUpTicks + CurrentBarDownTicks;
|
|
|
|
if(totalTicks > 0) {
|
|
OrderFlowDeltaPercent = ((double)CurrentBarUpTicks - (double)CurrentBarDownTicks) / totalTicks * 100.0;
|
|
} else {
|
|
OrderFlowDeltaPercent = 0;
|
|
}
|
|
|
|
CalculateVolumeEMAFromHistory();
|
|
DetectAbsorptionFromHistory();
|
|
|
|
CurrentBarUpTicks = 0;
|
|
CurrentBarDownTicks = 0;
|
|
CurrentBarVolume = 0;
|
|
CurrentBarHigh = SpotPrice;
|
|
CurrentBarLow = SpotPrice;
|
|
LastM1BarTime = newBarTime;
|
|
}
|
|
|
|
void CalculateVolumeEMAFromHistory() {
|
|
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() {
|
|
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;
|
|
} else if(buyAbsorptionCount >= requiredBars && IsPriceNearCallStrike()) {
|
|
CurrentAbsorptionState = ABSORPTION_BUY;
|
|
} else {
|
|
CurrentAbsorptionState = ABSORPTION_NONE;
|
|
}
|
|
}
|
|
|
|
void UpdateMarketData() {
|
|
SpotPrice = SymbolInfo.Bid();
|
|
SymbolInfo.RefreshRates();
|
|
|
|
if(InpOISource == OI_SOURCE_CSV_FILE) {
|
|
if(CachedFuturePrice < 0) {
|
|
LoadOIFromCSV();
|
|
}
|
|
FuturePrice = CachedFuturePrice;
|
|
} else {
|
|
FuturePrice = 0;
|
|
}
|
|
}
|
|
|
|
bool IsPriceNearPutStrike() {
|
|
double tolerance = InpOIZonePoints * _Point;
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
double strike = (i == 0) ? DynamicPutStrike1 : (i == 1) ? DynamicPutStrike2 : DynamicPutStrike3;
|
|
if(strike > 0 && MathAbs(SpotPrice - strike) <= tolerance) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsPriceNearCallStrike() {
|
|
double tolerance = InpOIZonePoints * _Point;
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
double strike = (i == 0) ? DynamicCallStrike1 : (i == 1) ? DynamicCallStrike2 : DynamicCallStrike3;
|
|
if(strike > 0 && MathAbs(SpotPrice - strike) <= tolerance) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsInMiddleOfRange() {
|
|
double tolerance = InpOIZonePoints * _Point * 2;
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
double putStrike = (i == 0) ? DynamicPutStrike1 : (i == 1) ? DynamicPutStrike2 : DynamicPutStrike3;
|
|
double callStrike = (i == 0) ? DynamicCallStrike1 : (i == 1) ? DynamicCallStrike2 : DynamicCallStrike3;
|
|
|
|
if(putStrike > 0 && MathAbs(SpotPrice - putStrike) <= tolerance) return false;
|
|
if(callStrike > 0 && MathAbs(SpotPrice - callStrike) <= tolerance) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CheckGlobalConditions() {
|
|
if(!TradingEnabled) return false;
|
|
|
|
if(CachedFuturePrice < 0) {
|
|
return false;
|
|
}
|
|
if(CachedFuturePrice == 0) {
|
|
return false;
|
|
}
|
|
|
|
if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return false;
|
|
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_ALLOWED)) return false;
|
|
|
|
long spread = SymbolInfo.Spread();
|
|
if(spread > InpMaxSpread) return false;
|
|
|
|
if(InpUseSessionFilter && !IsTradingSession()) return false;
|
|
|
|
if(InpUseATRFilter && !CheckVolatilityFilter()) return false;
|
|
|
|
if(!CheckDailyLimits()) {
|
|
TradingEnabled = false;
|
|
return false;
|
|
}
|
|
|
|
if(CheckEquityProtection()) {
|
|
TradingEnabled = false;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsTradingSession() {
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
return dt.hour >= InpSessionStartHour && dt.hour <= InpSessionEndHour;
|
|
}
|
|
|
|
bool CheckVolatilityFilter() {
|
|
if(ATRHandle == INVALID_HANDLE) return true;
|
|
|
|
double atrValues[];
|
|
ArraySetAsSeries(atrValues, true);
|
|
if(CopyBuffer(ATRHandle, 0, 0, 1, atrValues) < 1) return true;
|
|
|
|
double atrValue = atrValues[0];
|
|
double atrPercent = (atrValue / SpotPrice) * 100.0;
|
|
|
|
return atrPercent <= InpMaxATRPercent;
|
|
}
|
|
|
|
bool CheckDailyLimits() {
|
|
if(DailyTradeCount >= InpMaxDailyTrades) return false;
|
|
|
|
if(DailyLoss >= AccountInfo.Balance() * (InpMaxDailyLossPercent / 100.0)) return false;
|
|
if(DailyProfit >= AccountInfo.Balance() * (InpMaxDailyProfitPercent / 100.0)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CheckEquityProtection() {
|
|
double currentEquity = AccountInfo.Equity();
|
|
|
|
if(EquityHigh == 0) EquityHigh = currentEquity;
|
|
if(currentEquity > EquityHigh) EquityHigh = currentEquity;
|
|
|
|
double dropPercent = (EquityHigh - currentEquity) / EquityHigh * 100.0;
|
|
|
|
if(dropPercent >= InpEquityProtectionPercent) {
|
|
EquityLow = currentEquity;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CheckDailyReset() {
|
|
datetime now = TimeCurrent();
|
|
MqlDateTime dtNow, dtLast;
|
|
TimeToStruct(now, dtNow);
|
|
TimeToStruct(LastResetDate, dtLast);
|
|
|
|
if(LastResetDate == 0 || dtLast.day_of_week != dtNow.day_of_week) {
|
|
if(LastResetDate != 0 && dtLast.day != dtNow.day) {
|
|
DailyTradeCount = 0;
|
|
DailyPnL = 0;
|
|
DailyProfit = 0;
|
|
DailyLoss = 0;
|
|
ConsecutiveLosses = 0;
|
|
LastResetDate = now;
|
|
TradingEnabled = true;
|
|
Print("Daily statistics reset");
|
|
} else {
|
|
LastResetDate = now;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckTradingSignals() {
|
|
if(PositionsTotal() > 0) return;
|
|
|
|
if(InpUseAbsorptionFilter && IsInMiddleOfRange()) return;
|
|
|
|
ENUM_MARKET_PHASE marketPhase = GetMarketPhase();
|
|
|
|
if(CheckSellConditions(marketPhase)) {
|
|
ExecuteSellTrade();
|
|
} else if(CheckBuyConditions(marketPhase)) {
|
|
ExecuteBuyTrade();
|
|
}
|
|
}
|
|
|
|
ENUM_MARKET_PHASE GetMarketPhase() {
|
|
if(!InpUseMarketPhaseFilter) return PHASE_NEUTRAL;
|
|
|
|
double maFast[], maSlow[];
|
|
ArraySetAsSeries(maFast, true);
|
|
ArraySetAsSeries(maSlow, true);
|
|
|
|
if(CopyBuffer(MAFastHandle, 0, 0, 2, maFast) < 2) return PHASE_NEUTRAL;
|
|
if(CopyBuffer(MASlowHandle, 0, 0, 1, maSlow) < 1) return PHASE_NEUTRAL;
|
|
|
|
double maFastCurrent = maFast[0];
|
|
double maFastPrevious = maFast[1];
|
|
double maSlowCurrent = maSlow[0];
|
|
|
|
double slope = maFastCurrent - maFastPrevious;
|
|
|
|
if(maFastCurrent > maSlowCurrent && slope > 0) return PHASE_BULLISH;
|
|
else if(maFastCurrent < maSlowCurrent && slope < 0) return PHASE_BEARISH;
|
|
else if(MathAbs(slope) < 0.0001) return PHASE_SIDEWAYS;
|
|
else return PHASE_NEUTRAL;
|
|
}
|
|
|
|
bool CheckSellConditions(ENUM_MARKET_PHASE marketPhase) {
|
|
if(!IsPriceNearCallStrike()) return false;
|
|
|
|
if(InpUseAbsorptionFilter) {
|
|
if(CurrentAbsorptionState != ABSORPTION_BUY) return false;
|
|
}
|
|
|
|
if(!CheckOverbought()) return false;
|
|
if(!IsAtResistance()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CheckBuyConditions(ENUM_MARKET_PHASE marketPhase) {
|
|
if(!IsPriceNearPutStrike()) return false;
|
|
|
|
if(InpUseAbsorptionFilter) {
|
|
if(CurrentAbsorptionState != ABSORPTION_SELL) return false;
|
|
}
|
|
|
|
if(!CheckOversold()) return false;
|
|
if(!IsAtSupport()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CheckOverbought() {
|
|
if(RSIMainHandle != INVALID_HANDLE) {
|
|
double rsiValues[];
|
|
ArraySetAsSeries(rsiValues, true);
|
|
if(CopyBuffer(RSIMainHandle, 0, 0, 1, rsiValues) >= 1) {
|
|
if(rsiValues[0] < 70) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CheckOversold() {
|
|
if(RSIMainHandle != INVALID_HANDLE) {
|
|
double rsiValues[];
|
|
ArraySetAsSeries(rsiValues, true);
|
|
if(CopyBuffer(RSIMainHandle, 0, 0, 1, rsiValues) >= 1) {
|
|
if(rsiValues[0] > 30) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IsAtResistance() {
|
|
double tolerance = 100 * _Point;
|
|
|
|
if(MathAbs(SpotPrice - LevelUpper1) <= tolerance ||
|
|
MathAbs(SpotPrice - LevelUpper2) <= tolerance) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IsAtSupport() {
|
|
double tolerance = 100 * _Point;
|
|
|
|
if(MathAbs(SpotPrice - LevelLower1) <= tolerance ||
|
|
MathAbs(SpotPrice - LevelLower2) <= tolerance) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ExecuteBuyTrade() {
|
|
double lotSize = CalculateLotSize(POSITION_TYPE_BUY);
|
|
if(lotSize <= 0) return;
|
|
|
|
double sl = 0, tp = 0;
|
|
double nearestPutStrike = GetNearestPutStrike();
|
|
|
|
if(InpUseStopLoss && nearestPutStrike > 0) {
|
|
sl = nearestPutStrike - InpSLBufferPoints * _Point;
|
|
} else if(InpUseStopLoss) {
|
|
sl = SpotPrice - InpStopLossPoints * _Point;
|
|
}
|
|
|
|
if(InpUseTakeProfit) {
|
|
double nearestCallStrike = GetNearestCallStrike();
|
|
if(nearestCallStrike > 0) {
|
|
tp = nearestCallStrike;
|
|
} else {
|
|
tp = SpotPrice + InpTakeProfitPoints * _Point;
|
|
}
|
|
}
|
|
|
|
sl = NormalizeDouble(sl, _Digits);
|
|
tp = NormalizeDouble(tp, _Digits);
|
|
|
|
string comment = "OF_ABSORPTION_BUY";
|
|
|
|
if(Trade.Buy(lotSize, _Symbol, SpotPrice, sl, tp, comment)) {
|
|
Print("Buy order executed. Lot: ", lotSize, " SL: ", sl, " TP: ", tp);
|
|
DailyTradeCount++;
|
|
LastTradeTime = TimeCurrent();
|
|
} else {
|
|
Print("Buy order failed: ", Trade.ResultRetcodeDescription());
|
|
}
|
|
}
|
|
|
|
void ExecuteSellTrade() {
|
|
double lotSize = CalculateLotSize(POSITION_TYPE_SELL);
|
|
if(lotSize <= 0) return;
|
|
|
|
double sl = 0, tp = 0;
|
|
double nearestCallStrike = GetNearestCallStrike();
|
|
|
|
if(InpUseStopLoss && nearestCallStrike > 0) {
|
|
sl = nearestCallStrike + InpSLBufferPoints * _Point;
|
|
} else if(InpUseStopLoss) {
|
|
sl = SpotPrice + InpStopLossPoints * _Point;
|
|
}
|
|
|
|
if(InpUseTakeProfit) {
|
|
double nearestPutStrike = GetNearestPutStrike();
|
|
if(nearestPutStrike > 0) {
|
|
tp = nearestPutStrike;
|
|
} else {
|
|
tp = SpotPrice - InpTakeProfitPoints * _Point;
|
|
}
|
|
}
|
|
|
|
sl = NormalizeDouble(sl, _Digits);
|
|
tp = NormalizeDouble(tp, _Digits);
|
|
|
|
string comment = "OF_ABSORPTION_SELL";
|
|
|
|
if(Trade.Sell(lotSize, _Symbol, SpotPrice, sl, tp, comment)) {
|
|
Print("Sell order executed. Lot: ", lotSize, " SL: ", sl, " TP: ", tp);
|
|
DailyTradeCount++;
|
|
LastTradeTime = TimeCurrent();
|
|
} else {
|
|
Print("Sell order failed: ", Trade.ResultRetcodeDescription());
|
|
}
|
|
}
|
|
|
|
double GetNearestPutStrike() {
|
|
double nearest = 0;
|
|
double minDistance = DBL_MAX;
|
|
|
|
double strikes[3] = {DynamicPutStrike1, DynamicPutStrike2, DynamicPutStrike3};
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
if(strikes[i] > 0) {
|
|
double distance = MathAbs(SpotPrice - strikes[i]);
|
|
if(distance < minDistance) {
|
|
minDistance = distance;
|
|
nearest = strikes[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return nearest;
|
|
}
|
|
|
|
double GetNearestCallStrike() {
|
|
double nearest = 0;
|
|
double minDistance = DBL_MAX;
|
|
|
|
double strikes[3] = {DynamicCallStrike1, DynamicCallStrike2, DynamicCallStrike3};
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
if(strikes[i] > 0) {
|
|
double distance = MathAbs(SpotPrice - strikes[i]);
|
|
if(distance < minDistance) {
|
|
minDistance = distance;
|
|
nearest = strikes[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return nearest;
|
|
}
|
|
|
|
double CalculateLotSize(ENUM_POSITION_TYPE tradeType) {
|
|
if(!InpUseMoneyManagement) {
|
|
return NormalizeLot(InpLotSize);
|
|
}
|
|
|
|
double balance = AccountInfo.Balance();
|
|
double riskAmount = balance * (InpRiskPercent / 100.0);
|
|
|
|
double slDistance;
|
|
if(InpUseStopLoss) {
|
|
slDistance = InpStopLossPoints * _Point;
|
|
} else {
|
|
slDistance = 500 * _Point;
|
|
}
|
|
|
|
double tickValue = SymbolInfo.TickValue();
|
|
double tickSize = SymbolInfo.TickSize();
|
|
|
|
double lotSize = riskAmount / (slDistance * tickValue / tickSize);
|
|
|
|
lotSize = NormalizeLot(lotSize);
|
|
|
|
double maxLot = SymbolInfo.LotsMax();
|
|
double minLot = SymbolInfo.LotsMin();
|
|
|
|
if(lotSize > maxLot) lotSize = maxLot;
|
|
if(lotSize < minLot) lotSize = 0;
|
|
|
|
return lotSize;
|
|
}
|
|
|
|
double NormalizeLot(double lot) {
|
|
double step = SymbolInfo.LotsStep();
|
|
return MathFloor(lot / step) * step;
|
|
}
|
|
|
|
void ManagePositions() {
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
|
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
if(symbol != _Symbol) continue;
|
|
|
|
int magic = (int)PositionGetInteger(POSITION_MAGIC);
|
|
if(magic != InpMagicNumber) continue;
|
|
|
|
double currentProfit = PositionGetDouble(POSITION_PROFIT);
|
|
|
|
DailyPnL += currentProfit;
|
|
if(currentProfit > 0) DailyProfit += currentProfit;
|
|
else DailyLoss += currentProfit;
|
|
|
|
if(currentProfit < 0) {
|
|
ConsecutiveLosses++;
|
|
} else {
|
|
ConsecutiveLosses = 0;
|
|
}
|
|
|
|
if(InpCloseAllOnReverse && ConsecutiveLosses >= InpMaxConsecutiveLosses) {
|
|
Trade.PositionClose(PositionGetTicket(i));
|
|
TradingEnabled = false;
|
|
continue;
|
|
}
|
|
|
|
if(InpUseTrailingStop) {
|
|
ManageTrailingStop();
|
|
}
|
|
|
|
if(InpUseSoftStopLoss && currentProfit > 0) {
|
|
double sl = PositionGetDouble(POSITION_SL);
|
|
double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double distanceToEntry = MathAbs(SpotPrice - entryPrice);
|
|
|
|
if(distanceToEntry > InpTrailingStartPoints * _Point) {
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
double newSl = SpotPrice - InpTrailingStepPoints * _Point;
|
|
if(newSl > sl) {
|
|
Trade.PositionModify(PositionGetTicket(i), newSl, PositionGetDouble(POSITION_TP));
|
|
}
|
|
} else {
|
|
double newSl = SpotPrice + InpTrailingStepPoints * _Point;
|
|
if(newSl < sl || sl == 0) {
|
|
Trade.PositionModify(PositionGetTicket(i), newSl, PositionGetDouble(POSITION_TP));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ManageTrailingStop() {
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
|
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
if(symbol != _Symbol) continue;
|
|
|
|
int magic = (int)PositionGetInteger(POSITION_MAGIC);
|
|
if(magic != InpMagicNumber) continue;
|
|
|
|
double currentSl = PositionGetDouble(POSITION_SL);
|
|
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
double newSl = SpotPrice - InpTrailingStepPoints * _Point;
|
|
if(newSl > currentSl) {
|
|
Trade.PositionModify(PositionGetTicket(i), newSl, PositionGetDouble(POSITION_TP));
|
|
}
|
|
} else {
|
|
double newSl = SpotPrice + InpTrailingStepPoints * _Point;
|
|
if(newSl < currentSl || currentSl == 0) {
|
|
Trade.PositionModify(PositionGetTicket(i), newSl, PositionGetDouble(POSITION_TP));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CloseAllPositions() {
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
|
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
if(symbol != _Symbol) continue;
|
|
|
|
int magic = (int)PositionGetInteger(POSITION_MAGIC);
|
|
if(magic == InpMagicNumber) {
|
|
Trade.PositionClose(PositionGetTicket(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitializeOILevels() {
|
|
CallLevels[0] = DynamicCallStrike1;
|
|
CallLevels[1] = DynamicCallStrike2;
|
|
CallLevels[2] = DynamicCallStrike3;
|
|
|
|
PutLevels[0] = DynamicPutStrike1;
|
|
PutLevels[1] = DynamicPutStrike2;
|
|
PutLevels[2] = DynamicPutStrike3;
|
|
|
|
CallOI[0] = 0;
|
|
CallOI[1] = 0;
|
|
CallOI[2] = 0;
|
|
|
|
PutOI[0] = 0;
|
|
PutOI[1] = 0;
|
|
PutOI[2] = 0;
|
|
}
|
|
|
|
void InitializeKeyLevels() {
|
|
double allLevels[];
|
|
int count = 0;
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
if(CallLevels[i] > 0) {
|
|
ArrayResize(allLevels, count + 1);
|
|
allLevels[count] = CallLevels[i];
|
|
count++;
|
|
}
|
|
if(PutLevels[i] > 0) {
|
|
ArrayResize(allLevels, count + 1);
|
|
allLevels[count] = PutLevels[i];
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if(count > 0) {
|
|
ArraySort(allLevels);
|
|
|
|
LevelLower1 = allLevels[0];
|
|
LevelLower2 = (count > 1) ? allLevels[1] : allLevels[0];
|
|
LevelMid = (count > 2) ? allLevels[count/2] : allLevels[0];
|
|
LevelUpper2 = (count > 1) ? allLevels[count-1] : allLevels[0];
|
|
LevelUpper1 = (count > 2) ? allLevels[count-2] : allLevels[0];
|
|
}
|
|
}
|
|
|
|
bool InitializeIndicators() {
|
|
ATRHandle = iATR(_Symbol, PERIOD_H1, 14);
|
|
if(ATRHandle == INVALID_HANDLE) {
|
|
Print("Failed to create ATR indicator");
|
|
return false;
|
|
}
|
|
|
|
MAFastHandle = iMA(_Symbol, InpTrendTF, InpTrendMAPeriod1, 0, MODE_EMA, PRICE_CLOSE);
|
|
MASlowHandle = iMA(_Symbol, InpTrendTF, InpTrendMAPeriod2, 0, MODE_SMA, PRICE_CLOSE);
|
|
|
|
if(MAFastHandle == INVALID_HANDLE || MASlowHandle == INVALID_HANDLE) {
|
|
Print("Failed to create MA indicators");
|
|
return false;
|
|
}
|
|
|
|
RSIMainHandle = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE);
|
|
if(RSIMainHandle == INVALID_HANDLE) {
|
|
Print("Failed to create RSI indicator");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void LoadOIFromCSV()
|
|
{
|
|
Print("=== CSV Loading Debug ===");
|
|
Print("Terminal Files dir: ", TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\");
|
|
Print("Attempting to open: ", InpOICsvPath);
|
|
|
|
if(!FileIsExist(InpOICsvPath)) {
|
|
Print("CSV FILE NOT FOUND - check path or if scraper output exists");
|
|
return;
|
|
}
|
|
|
|
int filehandle = FileOpen(InpOICsvPath, FILE_READ | FILE_ANSI);
|
|
|
|
if(filehandle == INVALID_HANDLE)
|
|
{
|
|
int err = GetLastError();
|
|
Print("CSV OPEN FAILED [Error ", err, "]: ", err);
|
|
Print("File exists: ", FileIsExist(InpOICsvPath) ? "YES" : "NO");
|
|
return;
|
|
}
|
|
|
|
long fileSize = FileSize(filehandle);
|
|
Print("CSV opened successfully. Size: ", fileSize, " bytes");
|
|
|
|
int callIndex = 0;
|
|
int putIndex = 0;
|
|
double futurePrice = 0.0;
|
|
bool isFirstLine = true;
|
|
static bool csvParseErrorLogged = false;
|
|
|
|
while(!FileIsEnding(filehandle))
|
|
{
|
|
string row = FileReadString(filehandle);
|
|
|
|
Print("DEBUG ROW: [", row, "]");
|
|
|
|
if(isFirstLine) {
|
|
isFirstLine = false;
|
|
if(StringFind(row, "Type") >= 0 || StringFind(row, "Strike") >= 0 || StringFind(row, "OI") >= 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
string data[];
|
|
int count = StringSplit(row, ',', data);
|
|
|
|
if(count >= 3)
|
|
{
|
|
string type = data[0];
|
|
double strike = StringToDouble(data[1]);
|
|
int oi = (int)StringToInteger(data[2]);
|
|
|
|
if(StringFind(type, "Future") >= 0)
|
|
{
|
|
futurePrice = strike;
|
|
Print("DEBUG: Found Future row, price=", futurePrice);
|
|
}
|
|
else if(type == "CALL" && callIndex < 3)
|
|
{
|
|
CallLevels[callIndex] = strike;
|
|
CallOI[callIndex] = oi;
|
|
callIndex++;
|
|
Print("DEBUG: Found CALL row, strike=", strike, " oi=", oi);
|
|
}
|
|
else if(type == "PUT" && putIndex < 3)
|
|
{
|
|
PutLevels[putIndex] = strike;
|
|
PutOI[putIndex] = oi;
|
|
putIndex++;
|
|
Print("DEBUG: Found PUT row, strike=", strike, " oi=", oi);
|
|
}
|
|
}
|
|
}
|
|
|
|
FileClose(filehandle);
|
|
|
|
for(int i = callIndex; i < 3; i++) { CallLevels[i] = 0; CallOI[i] = 0; }
|
|
for(int i = putIndex; i < 3; i++) { PutLevels[i] = 0; PutOI[i] = 0; }
|
|
|
|
if(futurePrice > 0) {
|
|
CachedFuturePrice = futurePrice;
|
|
DynamicFuturePrice = futurePrice;
|
|
FuturePrice = futurePrice;
|
|
Print("CSV SUCCESS: FuturePrice=", futurePrice);
|
|
csvParseErrorLogged = false;
|
|
} else {
|
|
if(!csvParseErrorLogged) {
|
|
Print("CSV ERROR: No valid Future price found in file");
|
|
csvParseErrorLogged = true;
|
|
}
|
|
}
|
|
|
|
InitializeKeyLevels();
|
|
}
|
|
|
|
void CheckExistingPositions() {
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
if(PositionSelectByTicket(PositionGetTicket(i))) {
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
if(symbol == _Symbol) {
|
|
int magic = (int)PositionGetInteger(POSITION_MAGIC);
|
|
if(magic == InpMagicNumber) {
|
|
Print("Existing position found: ", PositionGetInteger(POSITION_TYPE));
|
|
DailyTradeCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CreateDashboard() {
|
|
int panelWidth = 280;
|
|
int panelHeight = 300;
|
|
|
|
CreatePanel("DashboardPanel", 10, 10, panelWidth, panelHeight, C'25,25,35', BORDER_FLAT);
|
|
|
|
CreateLabel("DB_Title", 20, 20, "ORDER FLOW ABSORPTION EA", clrYellow, 10);
|
|
|
|
UpdateDashboard();
|
|
}
|
|
|
|
void UpdateDashboard() {
|
|
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);
|
|
UpdateLabel("DB_Delta", 20, 85, "Delta: " + deltaText, deltaColor, 8);
|
|
|
|
string absorptionText = "";
|
|
color absorptionColor = clrGray;
|
|
|
|
switch(CurrentAbsorptionState) {
|
|
case ABSORPTION_BUY:
|
|
absorptionText = "BUY ABSORPTION";
|
|
absorptionColor = clrLime;
|
|
break;
|
|
case ABSORPTION_SELL:
|
|
absorptionText = "SELL ABSORPTION";
|
|
absorptionColor = clrRed;
|
|
break;
|
|
default:
|
|
absorptionText = "NONE";
|
|
break;
|
|
}
|
|
|
|
UpdateLabel("DB_Absorption", 20, 105, "Absorption: " + absorptionText, absorptionColor, 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;
|
|
UpdateLabel("DB_PriceDrift", 20, 145, "Drift: " + driftText, driftColor, 8);
|
|
|
|
string csvText = "";
|
|
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_Trades", 20, 180, "Daily: " + IntegerToString(DailyTradeCount) +
|
|
"/" + IntegerToString(InpMaxDailyTrades), clrWhite, 8);
|
|
|
|
UpdateLabel("DB_PnL", 20, 200, "Daily PnL: " + DoubleToString(DailyPnL, 2),
|
|
DailyPnL >= 0 ? clrLime : clrRed, 8);
|
|
|
|
string tradingStatus = TradingEnabled ? "ENABLED" : "DISABLED";
|
|
color statusColor = TradingEnabled ? clrLime : clrRed;
|
|
UpdateLabel("DB_Status", 20, 220, "Trading: " + tradingStatus, statusColor, 8);
|
|
|
|
string nearZone = "";
|
|
if(IsPriceNearPutStrike()) nearZone = "NEAR PUT";
|
|
else if(IsPriceNearCallStrike()) nearZone = "NEAR CALL";
|
|
else nearZone = "MIDDLE";
|
|
|
|
color zoneColor = (nearZone == "MIDDLE") ? clrOrange : clrCyan;
|
|
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() {
|
|
CreateControlPanelUI();
|
|
UpdateControlPanelValues();
|
|
}
|
|
|
|
void CreateControlPanelUI() {
|
|
int panelWidth = 850;
|
|
int topPanelHeight = 50;
|
|
int oiPanelHeight = 120;
|
|
|
|
CreatePanel("CP_TopPanel", ControlPanelX, ControlPanelY, panelWidth, topPanelHeight, C'45,45,45', BORDER_FLAT);
|
|
CreateButton("CP_CloseAll", ControlPanelX + 10, ControlPanelY + 10, 830, 30, "Close All Positions", clrRed, clrWhite);
|
|
|
|
CreatePanel("CP_OIDataPanel", ControlPanelX, ControlPanelY + topPanelHeight + 10, panelWidth, oiPanelHeight, C'45,45,45', BORDER_FLAT);
|
|
|
|
CreateLabel("CP_FuturePriceLabel", ControlPanelX + 10, ControlPanelY + topPanelHeight + 20, "Future Price:", clrYellow, 8);
|
|
CreateLabel("CP_CallStrikesLabel", ControlPanelX + 200, ControlPanelY + topPanelHeight + 20, "Call Strikes:", clrYellow, 8);
|
|
CreateLabel("CP_PutStrikesLabel", ControlPanelX + 200, ControlPanelY + topPanelHeight + 50, "Put Strikes:", clrYellow, 8);
|
|
|
|
CreateEditField("CP_FuturePrice", ControlPanelX + 90, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateEditField("CP_CallStrike1", ControlPanelX + 280, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateEditField("CP_CallStrike2", ControlPanelX + 370, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateEditField("CP_CallStrike3", ControlPanelX + 460, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateEditField("CP_PutStrike1", ControlPanelX + 280, ControlPanelY + topPanelHeight + 45, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateEditField("CP_PutStrike2", ControlPanelX + 370, ControlPanelY + topPanelHeight + 45, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateEditField("CP_PutStrike3", ControlPanelX + 460, ControlPanelY + topPanelHeight + 45, 80, 20, "0", clrWhite, clrBlack);
|
|
CreateButton("CP_UpdateOI", ControlPanelX + 750, ControlPanelY + topPanelHeight + 25, 90, 35, "Update OI Data", clrCyan, clrBlack);
|
|
}
|
|
|
|
void CreatePanel(string name, int x, int y, int width, int height, color bgColor, int borderType) {
|
|
if(ObjectFind(chart_id, name) < 0) {
|
|
ObjectCreate(chart_id, name, OBJ_RECTANGLE_LABEL, 0, 0, 0, 0, 0);
|
|
}
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XSIZE, width);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YSIZE, height);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_BGCOLOR, bgColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_BORDER_TYPE, borderType);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1000);
|
|
}
|
|
|
|
void CreateButton(string name, int x, int y, int width, int height, string text, color bgColor, color textColor) {
|
|
if(ObjectFind(chart_id, name) < 0) {
|
|
ObjectCreate(chart_id, name, OBJ_BUTTON, 0, 0, 0, 0, 0);
|
|
}
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XSIZE, width);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YSIZE, height);
|
|
ObjectSetString(chart_id, name, OBJPROP_TEXT, text);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_BGCOLOR, bgColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, textColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, 10);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1001);
|
|
}
|
|
|
|
void CreateLabel(string name, int x, int y, string text, color textColor, int fontSize) {
|
|
if(ObjectFind(chart_id, name) < 0) {
|
|
ObjectCreate(chart_id, name, OBJ_LABEL, 0, 0, 0, 0, 0);
|
|
}
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y);
|
|
ObjectSetString(chart_id, name, OBJPROP_TEXT, text);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, textColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, fontSize);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1002);
|
|
}
|
|
|
|
void CreateEditField(string name, int x, int y, int width, int height, string defaultText, color bgColor, color textColor) {
|
|
if(ObjectFind(chart_id, name) < 0) {
|
|
ObjectCreate(chart_id, name, OBJ_EDIT, 0, 0, 0, 0, 0);
|
|
}
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XSIZE, width);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YSIZE, height);
|
|
ObjectSetString(chart_id, name, OBJPROP_TEXT, defaultText);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_BGCOLOR, bgColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, textColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, 10);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1003);
|
|
}
|
|
|
|
void UpdateLabel(string name, int x, int y, string text, color textColor, int fontSize) {
|
|
if(ObjectFind(chart_id, name) < 0) {
|
|
CreateLabel(name, x, y, text, textColor, fontSize);
|
|
} else {
|
|
ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y);
|
|
ObjectSetString(chart_id, name, OBJPROP_TEXT, text);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, textColor);
|
|
ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, fontSize);
|
|
}
|
|
}
|
|
|
|
void UpdateControlPanel() {
|
|
UpdateEditField("CP_FuturePrice", DoubleToString(DynamicFuturePrice, 2));
|
|
UpdateEditField("CP_CallStrike1", DoubleToString(DynamicCallStrike1, 2));
|
|
UpdateEditField("CP_CallStrike2", DoubleToString(DynamicCallStrike2, 2));
|
|
UpdateEditField("CP_CallStrike3", DoubleToString(DynamicCallStrike3, 2));
|
|
UpdateEditField("CP_PutStrike1", DoubleToString(DynamicPutStrike1, 2));
|
|
UpdateEditField("CP_PutStrike2", DoubleToString(DynamicPutStrike2, 2));
|
|
UpdateEditField("CP_PutStrike3", DoubleToString(DynamicPutStrike3, 2));
|
|
}
|
|
|
|
void UpdateEditField(string name, string value) {
|
|
if(ObjectFind(chart_id, name) >= 0) {
|
|
ObjectSetString(chart_id, name, OBJPROP_TEXT, value);
|
|
}
|
|
}
|
|
|
|
void UpdateControlPanelValues() {
|
|
UpdateEditField("CP_FuturePrice", DoubleToString(DynamicFuturePrice, 2));
|
|
UpdateEditField("CP_CallStrike1", DoubleToString(DynamicCallStrike1, 2));
|
|
UpdateEditField("CP_CallStrike2", DoubleToString(DynamicCallStrike2, 2));
|
|
UpdateEditField("CP_CallStrike3", DoubleToString(DynamicCallStrike3, 2));
|
|
UpdateEditField("CP_PutStrike1", DoubleToString(DynamicPutStrike1, 2));
|
|
UpdateEditField("CP_PutStrike2", DoubleToString(DynamicPutStrike2, 2));
|
|
UpdateEditField("CP_PutStrike3", DoubleToString(DynamicPutStrike3, 2));
|
|
}
|
|
|
|
void HandleControlPanelClick(string name) {
|
|
if(name == "CP_CloseAll") {
|
|
CloseAllPositions();
|
|
}
|
|
else if(name == "CP_UpdateOI") {
|
|
UpdateInputValues();
|
|
InitializeOILevels();
|
|
InitializeKeyLevels();
|
|
Print("OI Levels Updated");
|
|
}
|
|
}
|
|
|
|
void UpdateInputValues() {
|
|
string value;
|
|
|
|
if(ObjectGetString(chart_id, "CP_FuturePrice", OBJPROP_TEXT, 0, value)) {
|
|
DynamicFuturePrice = StringToDouble(value);
|
|
}
|
|
|
|
if(ObjectGetString(chart_id, "CP_CallStrike1", OBJPROP_TEXT, 0, value)) {
|
|
DynamicCallStrike1 = StringToDouble(value);
|
|
}
|
|
if(ObjectGetString(chart_id, "CP_CallStrike2", OBJPROP_TEXT, 0, value)) {
|
|
DynamicCallStrike2 = StringToDouble(value);
|
|
}
|
|
if(ObjectGetString(chart_id, "CP_CallStrike3", OBJPROP_TEXT, 0, value)) {
|
|
DynamicCallStrike3 = StringToDouble(value);
|
|
}
|
|
if(ObjectGetString(chart_id, "CP_PutStrike1", OBJPROP_TEXT, 0, value)) {
|
|
DynamicPutStrike1 = StringToDouble(value);
|
|
}
|
|
if(ObjectGetString(chart_id, "CP_PutStrike2", OBJPROP_TEXT, 0, value)) {
|
|
DynamicPutStrike2 = StringToDouble(value);
|
|
}
|
|
if(ObjectGetString(chart_id, "CP_PutStrike3", OBJPROP_TEXT, 0, value)) {
|
|
DynamicPutStrike3 = StringToDouble(value);
|
|
}
|
|
|
|
InitializeOILevels();
|
|
InitializeKeyLevels();
|
|
|
|
if(InpOISource == OI_SOURCE_CSV_FILE) {
|
|
CachedFuturePrice = -1;
|
|
LoadOIFromCSV();
|
|
}
|
|
} |