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.
1310 lines
40 KiB
Plaintext
1310 lines
40 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_MANUAL;
|
|
input string InpOICsvPath = "\\Files\\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;
|
|
|
|
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];
|
|
|
|
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; // -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() {
|
|
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(!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();
|
|
}
|
|
|
|
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();
|
|
|
|
FuturePrice = LoadFuturePriceFromCSV();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
double LoadFuturePriceFromCSV() {
|
|
if(CachedFuturePrice >= 0) {
|
|
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(!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;
|
|
}
|
|
|
|
double futurePrice = 0.0;
|
|
int dataLineCount = 0;
|
|
|
|
while(!FileIsEnding(filehandle)) {
|
|
string line = FileReadString(filehandle);
|
|
dataLineCount++;
|
|
|
|
if(line == "") continue;
|
|
|
|
string parts[];
|
|
int split = StringSplit(line, ',', parts);
|
|
|
|
if(split >= 2) {
|
|
double price = StringToDouble(parts[1]);
|
|
if(price > 0) {
|
|
futurePrice = price;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FileClose(filehandle);
|
|
|
|
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() {
|
|
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() {
|
|
DynamicFuturePrice = StringToDouble(ObjectGetString(chart_id, "CP_FuturePrice", OBJPROP_TEXT));
|
|
DynamicCallStrike1 = StringToDouble(ObjectGetString(chart_id, "CP_CallStrike1", OBJPROP_TEXT));
|
|
DynamicCallStrike2 = StringToDouble(ObjectGetString(chart_id, "CP_CallStrike2", OBJPROP_TEXT));
|
|
DynamicCallStrike3 = StringToDouble(ObjectGetString(chart_id, "CP_CallStrike3", OBJPROP_TEXT));
|
|
DynamicPutStrike1 = StringToDouble(ObjectGetString(chart_id, "CP_PutStrike1", OBJPROP_TEXT));
|
|
DynamicPutStrike2 = StringToDouble(ObjectGetString(chart_id, "CP_PutStrike2", OBJPROP_TEXT));
|
|
DynamicPutStrike3 = StringToDouble(ObjectGetString(chart_id, "CP_PutStrike3", OBJPROP_TEXT));
|
|
}
|