//+------------------------------------------------------------------+ //| Cosmic Scalper EA | //| Based on Cosmic Scalper Indicator logic | //| All calculations internal - no iCustom / no chart drawing | //+------------------------------------------------------------------+ #property copyright "Cosmic Scalper EA" #property version "1.00" #property strict #include //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ // --- Lot & Risk --- input double InpLotSize = 0.10; // Fixed Lot Size input double InpTP_Points = 300.0; // Take Profit (points) input double InpSL_Points = 300.0; // Stop Loss (points) // --- Cosmic Pulse multipliers (mirrors indicator defaults) --- input double InpStarMultiplier1 = 1.0; // Pulse 1 Stellar Multiplier input double InpStarMultiplier2 = 4.0; // Pulse 2 Stellar Multiplier input double InpStarMultiplier3 = 7.0; // Pulse 3 Stellar Multiplier // --- Shared Pulse Settings --- input int InpQuantumPeriod = 14; // ATR Short Period input int InpQuantumLongPer = 13; // ATR Long Period input int InpGalaxyLookBack = 50; // High/Low Lookback (bars) input double InpNebulaRatio = 0.618; // Nebula Ratio // --- Nova Signal Settings --- input int InpStellarFast = 5; // EMA Fast Period input int InpStellarSlow = 10; // EMA Slow Period input int InpMinNovaScore = 3; // Min Nova Score to trigger // --- Nova ATR Offset (internal, not drawn) --- input int InpNovaQuantumPer = 50; // ATR Period for Nova Offset // --- Trade Settings --- input ulong InpMagicNumber = 202501; // EA Magic Number input bool InpOneTradePerBar = true; // One Trade Per Bar input int InpBarsBack = 500; // Bars of history to calculate //+------------------------------------------------------------------+ //| Globals - indicator handles | //+------------------------------------------------------------------+ CTrade Trade; int g_hATRShort0, g_hATRShort1, g_hATRShort2; int g_hATRLong0, g_hATRLong1, g_hATRLong2; int g_hATRNova; int g_hEMAFast; int g_hEMASlow; // --- Flat internal arrays (one per pulse, series order) --- double g_drift0[], g_drift1[], g_drift2[]; int g_trend0[], g_trend1[], g_trend2[]; datetime g_lastBarTime = 0; bool g_tradedThisBar = false; //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { Trade.SetExpertMagicNumber(InpMagicNumber); Trade.SetDeviationInPoints(10); g_hATRShort0 = iATR(_Symbol, _Period, InpQuantumPeriod); g_hATRLong0 = iATR(_Symbol, _Period, InpQuantumLongPer); g_hATRShort1 = iATR(_Symbol, _Period, InpQuantumPeriod); g_hATRLong1 = iATR(_Symbol, _Period, InpQuantumLongPer); g_hATRShort2 = iATR(_Symbol, _Period, InpQuantumPeriod); g_hATRLong2 = iATR(_Symbol, _Period, InpQuantumLongPer); g_hATRNova = iATR(_Symbol, _Period, InpNovaQuantumPer); g_hEMAFast = iMA(_Symbol, _Period, InpStellarFast, 0, MODE_EMA, PRICE_CLOSE); g_hEMASlow = iMA(_Symbol, _Period, InpStellarSlow, 0, MODE_EMA, PRICE_CLOSE); if(g_hATRShort0 == INVALID_HANDLE || g_hATRLong0 == INVALID_HANDLE || g_hATRShort1 == INVALID_HANDLE || g_hATRLong1 == INVALID_HANDLE || g_hATRShort2 == INVALID_HANDLE || g_hATRLong2 == INVALID_HANDLE || g_hATRNova == INVALID_HANDLE || g_hEMAFast == INVALID_HANDLE || g_hEMASlow == INVALID_HANDLE) { Print("CosmicScalperEA: Failed to create indicator handles."); return INIT_FAILED; } int sz = InpBarsBack + 20; ArrayResize(g_drift0, sz); ArraySetAsSeries(g_drift0, true); ArrayInitialize(g_drift0, 0.0); ArrayResize(g_drift1, sz); ArraySetAsSeries(g_drift1, true); ArrayInitialize(g_drift1, 0.0); ArrayResize(g_drift2, sz); ArraySetAsSeries(g_drift2, true); ArrayInitialize(g_drift2, 0.0); ArrayResize(g_trend0, sz); ArraySetAsSeries(g_trend0, true); ArrayInitialize(g_trend0, 0); ArrayResize(g_trend1, sz); ArraySetAsSeries(g_trend1, true); ArrayInitialize(g_trend1, 0); ArrayResize(g_trend2, sz); ArraySetAsSeries(g_trend2, true); ArrayInitialize(g_trend2, 0); Print("CosmicScalperEA initialised OK."); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(g_hATRShort0 != INVALID_HANDLE) IndicatorRelease(g_hATRShort0); if(g_hATRShort1 != INVALID_HANDLE) IndicatorRelease(g_hATRShort1); if(g_hATRShort2 != INVALID_HANDLE) IndicatorRelease(g_hATRShort2); if(g_hATRLong0 != INVALID_HANDLE) IndicatorRelease(g_hATRLong0); if(g_hATRLong1 != INVALID_HANDLE) IndicatorRelease(g_hATRLong1); if(g_hATRLong2 != INVALID_HANDLE) IndicatorRelease(g_hATRLong2); if(g_hATRNova != INVALID_HANDLE) IndicatorRelease(g_hATRNova); if(g_hEMAFast != INVALID_HANDLE) IndicatorRelease(g_hEMAFast); if(g_hEMASlow != INVALID_HANDLE) IndicatorRelease(g_hEMASlow); } //+------------------------------------------------------------------+ //| Read one value from an indicator buffer at shift | //+------------------------------------------------------------------+ double GetBuf(int handle, int shift) { if(handle == INVALID_HANDLE) return 0.0; double b[1]; if(CopyBuffer(handle, 0, shift, 1, b) == 1) return b[0]; return 0.0; } //+------------------------------------------------------------------+ //| Index of highest High from startIdx to startIdx+lookback | //+------------------------------------------------------------------+ int FindHighIdx(const double &h[], int startIdx, int lookback, int total) { int best = startIdx; double val = (startIdx < total) ? h[startIdx] : 0.0; int end = MathMin(startIdx + lookback, total - 1); for(int k = startIdx + 1; k <= end; k++) if(h[k] > val) { val = h[k]; best = k; } return best; } //+------------------------------------------------------------------+ //| Index of lowest Low from startIdx to startIdx+lookback | //+------------------------------------------------------------------+ int FindLowIdx(const double &l[], int startIdx, int lookback, int total) { int best = startIdx; double val = (startIdx < total) ? l[startIdx] : DBL_MAX; int end = MathMin(startIdx + lookback, total - 1); for(int k = startIdx + 1; k <= end; k++) if(l[k] < val) { val = l[k]; best = k; } return best; } //+------------------------------------------------------------------+ //| Calculate drift + trend for one pulse at bar i | //+------------------------------------------------------------------+ void CalcPulseDrift(int i, int total, double starMult, int hShort, int hLong, const double &high[], const double &low[], const double &close[], double &drift[], int &trend[]) { int dSz = ArraySize(drift); int tSz = ArraySize(trend); if(i < 0 || i >= total || i >= dSz) return; double qShort = GetBuf(hShort, i + 1); double qLong = GetBuf(hLong, i + 1); double dynMult = starMult * (qLong > 0.0 ? qShort / qLong : 1.0); int hiIdx = FindHighIdx(high, i + 1, InpGalaxyLookBack, total); int loIdx = FindLowIdx (low, i + 1, InpGalaxyLookBack, total); if(hiIdx < 0 || hiIdx >= total || loIdx < 0 || loIdx >= total) return; double peak = high[hiIdx]; double trough = low[loIdx]; double prevDrift = (i + 1 < dSz) ? drift[i + 1] : 0.0; int prevTrend = (i + 1 < tSz) ? trend[i + 1] : 0; int curTrend; int i2 = MathMin(i + 2, total - 1); if(i + 2 >= total) curTrend = (close[i + 1] > close[i2]) ? 1 : -1; else curTrend = (close[i + 1] > prevDrift) ? 1 : (close[i + 1] < prevDrift) ? -1 : prevTrend; if(i < tSz) trend[i] = curTrend; double newDrift; if(curTrend == 1) { double nebulaLvl = trough + (peak - trough) * InpNebulaRatio; double qLow = low[i] - dynMult * qShort; double stopLvl = qLow + (nebulaLvl - qLow) * 0.5; newDrift = (prevDrift > 0.0) ? MathMax(stopLvl, prevDrift) : stopLvl; } else { double nebulaLvl = peak - (peak - trough) * InpNebulaRatio; double qHigh = high[i] + dynMult * qShort; double stopLvl = qHigh + (nebulaLvl - qHigh) * 0.5; newDrift = (prevDrift > 0.0) ? MathMin(stopLvl, prevDrift) : stopLvl; } if(i < dSz) drift[i] = newDrift; } //+------------------------------------------------------------------+ //| Nova signal: +1 buy, -1 sell, 0 none | //+------------------------------------------------------------------+ int CalcNovaSignal(int i, int total, const double &open[], const double &high[], const double &low[], const double &close[], const long &vol[]) { if(i < 0 || i >= total) return 0; int score = 0; double body = MathAbs(close[i] - open[i]); double upperWick = high[i] - MathMax(close[i], open[i]); double lowerWick = MathMin(close[i], open[i]) - low[i]; if(lowerWick > 2.0 * body) score++; else if(upperWick > 2.0 * body) score--; double avgVol = 0.0; int vCnt = 0; for(int j = 1; j <= 5 && (i + j) < total; j++) { avgVol += (double)vol[i + j]; vCnt++; } if(vCnt > 0) avgVol /= (double)vCnt; else avgVol = 1.0; if((double)vol[i] > avgVol) score += (close[i] > open[i] ? 1 : -1); double avgBody = 0.0; int bCnt = 0; for(int j = 1; j <= 5 && (i + j) < total; j++) { avgBody += MathAbs(close[i + j] - open[i + j]); bCnt++; } if(bCnt > 0) avgBody /= (double)bCnt; else avgBody = 1.0; if(body > avgBody) score += (close[i] > open[i] ? 1 : -1); double mid = 0.0; int mCnt = 0; for(int j = 1; j <= 5 && (i + j) < total; j++) { mid += close[i + j]; mCnt++; } if(mCnt > 0) mid /= (double)mCnt; else mid = close[i]; score += (close[i] > mid ? 1 : -1); double fast = GetBuf(g_hEMAFast, i); double slow = GetBuf(g_hEMASlow, i); if(fast > slow) score++; else if(fast < slow) score--; if(score >= InpMinNovaScore) return 1; if(score <= -InpMinNovaScore) return -1; return 0; } //+------------------------------------------------------------------+ //| True if position already open on this symbol/magic | //+------------------------------------------------------------------+ bool HasOpenPosition() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetTicket(i) == 0) continue; if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == (long)InpMagicNumber) return true; } return false; } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { // Gate calculations to new bar only datetime barTime = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); bool newBar = (barTime != g_lastBarTime); if(newBar) { g_lastBarTime = barTime; g_tradedThisBar = false; } if(InpOneTradePerBar && g_tradedThisBar) return; if(HasOpenPosition()) return; // --- Copy price data --- int total = (int)SeriesInfoInteger(_Symbol, _Period, SERIES_BARS_COUNT); int need = MathMin(InpBarsBack + InpGalaxyLookBack + InpQuantumLongPer + 20, total); if(total < 100) return; double Open[], High[], Low[], Close[]; long Vol[]; ArraySetAsSeries(Open, true); ArraySetAsSeries(High, true); ArraySetAsSeries(Low, true); ArraySetAsSeries(Close, true); ArraySetAsSeries(Vol, true); if(CopyOpen (_Symbol, _Period, 0, need, Open) <= 0) return; if(CopyHigh (_Symbol, _Period, 0, need, High) <= 0) return; if(CopyLow (_Symbol, _Period, 0, need, Low) <= 0) return; if(CopyClose (_Symbol, _Period, 0, need, Close) <= 0) return; if(CopyTickVolume (_Symbol, _Period, 0, need, Vol) <= 0) return; int bars = ArraySize(Close); // Grow internal arrays if needed if(ArraySize(g_drift0) < bars + 2) { ArrayResize(g_drift0, bars + 20); ArraySetAsSeries(g_drift0, true); } if(ArraySize(g_drift1) < bars + 2) { ArrayResize(g_drift1, bars + 20); ArraySetAsSeries(g_drift1, true); } if(ArraySize(g_drift2) < bars + 2) { ArrayResize(g_drift2, bars + 20); ArraySetAsSeries(g_drift2, true); } if(ArraySize(g_trend0) < bars + 2) { ArrayResize(g_trend0, bars + 20); ArraySetAsSeries(g_trend0, true); } if(ArraySize(g_trend1) < bars + 2) { ArrayResize(g_trend1, bars + 20); ArraySetAsSeries(g_trend1, true); } if(ArraySize(g_trend2) < bars + 2) { ArrayResize(g_trend2, bars + 20); ArraySetAsSeries(g_trend2, true); } // --- Recalculate pulse drifts oldest -> newest --- for(int i = bars - 1; i >= 1; i--) { CalcPulseDrift(i, bars, InpStarMultiplier1, g_hATRShort0, g_hATRLong0, High, Low, Close, g_drift0, g_trend0); CalcPulseDrift(i, bars, InpStarMultiplier2, g_hATRShort1, g_hATRLong1, High, Low, Close, g_drift1, g_trend1); CalcPulseDrift(i, bars, InpStarMultiplier3, g_hATRShort2, g_hATRLong2, High, Low, Close, g_drift2, g_trend2); } // --- Check signal on last closed bar (index 1) --- int eb = 1; int t0 = (eb < ArraySize(g_trend0)) ? g_trend0[eb] : 0; int t1 = (eb < ArraySize(g_trend1)) ? g_trend1[eb] : 0; int t2 = (eb < ArraySize(g_trend2)) ? g_trend2[eb] : 0; double d0 = (eb < ArraySize(g_drift0)) ? g_drift0[eb] : 0.0; double d1 = (eb < ArraySize(g_drift1)) ? g_drift1[eb] : 0.0; double d2 = (eb < ArraySize(g_drift2)) ? g_drift2[eb] : 0.0; int novaSignal = CalcNovaSignal(eb, bars, Open, High, Low, Close, Vol); bool allBull = (t0 == 1 && t1 == 1 && t2 == 1); bool allBear = (t0 == -1 && t1 == -1 && t2 == -1); // Bullish stack: Pulse1 < Pulse2 < Pulse3 (support lines ascending under price) bool bullOrder = allBull && (d0 < d1) && (d1 < d2); // Bearish stack: Pulse1 > Pulse2 > Pulse3 (resistance lines descending above price) bool bearOrder = allBear && (d0 > d1) && (d1 > d2); bool buySetup = bullOrder && (novaSignal == 1); bool sellSetup = bearOrder && (novaSignal == -1); if(!buySetup && !sellSetup) return; // --- Open trade --- double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(buySetup) { double sl = NormalizeDouble(ask - InpSL_Points * pt, _Digits); double tp = NormalizeDouble(ask + InpTP_Points * pt, _Digits); if(Trade.Buy(InpLotSize, _Symbol, ask, sl, tp, "CosmicScalper Buy")) { PrintFormat("BUY Ask=%.5f SL=%.5f TP=%.5f | T=%d,%d,%d D=%.5f,%.5f,%.5f", ask, sl, tp, t0, t1, t2, d0, d1, d2); g_tradedThisBar = true; } else PrintFormat("BUY FAILED %d: %s", Trade.ResultRetcode(), Trade.ResultRetcodeDescription()); } else { double sl = NormalizeDouble(bid + InpSL_Points * pt, _Digits); double tp = NormalizeDouble(bid - InpTP_Points * pt, _Digits); if(Trade.Sell(InpLotSize, _Symbol, bid, sl, tp, "CosmicScalper Sell")) { PrintFormat("SELL Bid=%.5f SL=%.5f TP=%.5f | T=%d,%d,%d D=%.5f,%.5f,%.5f", bid, sl, tp, t0, t1, t2, d0, d1, d2); g_tradedThisBar = true; } else PrintFormat("SELL FAILED %d: %s", Trade.ResultRetcode(), Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+