From 6aaed9d03af56bc588959fd2d8fa8730738b9794 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Fri, 30 May 2025 14:22:12 +0330 Subject: [PATCH] last ... --- Documents/BKP/1.mq5 | 243 +++++++++++ Experts/xcatb.test.ea.mq5 | 24 +- Libraries/x-saherelm.x-trade.lib.mq5 | 26 ++ XCATBEA/Classes/xcatbea.signaller.class.mq5 | 32 ++ .../xcatbea.x-data.collector.class.mq5 | 46 +- .../Classes/xcatbea.x-trade.manager.class.mq5 | 396 +++++++++++++++++- 6 files changed, 747 insertions(+), 20 deletions(-) create mode 100644 Documents/BKP/1.mq5 create mode 100644 XCATBEA/Classes/xcatbea.signaller.class.mq5 diff --git a/Documents/BKP/1.mq5 b/Documents/BKP/1.mq5 new file mode 100644 index 00000000..4bd1dda1 --- /dev/null +++ b/Documents/BKP/1.mq5 @@ -0,0 +1,243 @@ +//+------------------------------------------------------------------+ +//| SpikeDetectorEA.mq5 | +//| Copyright 2025, Your Name/Company | +//| https://www.example.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, Your Name/Company" +#property link "https://www.example.com" +#property version "1.01" // Incremented version +#property description "Expert Advisor to detect and draw price spikes (pin bars)." + +//--- Input Parameters +input group "Spike Definition" input double InpMinWickToBodyRatio = 2.0; // Minimum ratio of the main wick to the body (e.g., 2.0 means wick is 2x body) +input double InpMaxOppositeWickToBodyRatio = 1.0; // Maximum ratio of the opposite wick to the body (e.g., 1.0 means opposite wick <= body) +input double InpMaxBodyToTotalRangeRatio = 0.33; // Maximum ratio of body to total candle range (H-L) (e.g., 0.33 means body is < 1/3 of range) + +input group "Drawing Settings" input int InpArrowOffsetPoints = 10; // Arrow offset from High/Low in points +input color InpBullishSpikeColor = clrLimeGreen; // Color for bullish spike arrows +input color InpBearishSpikeColor = clrRed; // Color for bearish spike arrows +input int InpLookbackForDrawing = 200; // How many past bars to check on EA load/recompile + +// Wingdings Arrow Codes (commonly used for OBJ_ARROW) +#define WINGDINGS_ARROW_UP 241 +#define WINGDINGS_ARROW_DOWN 242 + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + //--- + // You can add any one-time initialization logic here if needed + Print("SpikeDetectorEA initialized. Symbol: ", _Symbol, ", Timeframe: ", EnumToString((ENUM_TIMEFRAMES)Period())); + //--- + return (INIT_SUCCEEDED); +} +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + //--- + // Optional: Clean up objects when EA is removed or chart is closed + // For more targeted deletion, you might loop through objects and check names + // ObjectsDeleteAll(0, "SpikeArrow_Bearish_" + _Symbol); + // ObjectsDeleteAll(0, "SpikeArrow_Bullish_" + _Symbol); + // A more robust way if you have many EAs or indicators: + long chart_id = ChartID(); + for (int i = ObjectsTotal(chart_id, -1, OBJ_ARROW) - 1; i >= 0; i--) + { + string obj_name = ObjectName(chart_id, i, -1, OBJ_ARROW); + if (StringFind(obj_name, "SpikeArrow_Bearish_" + _Symbol + "_" + EnumToString((ENUM_TIMEFRAMES)Period())) == 0 || + StringFind(obj_name, "SpikeArrow_Bullish_" + _Symbol + "_" + EnumToString((ENUM_TIMEFRAMES)Period())) == 0) + { + ObjectDelete(chart_id, obj_name); + } + } + Print("SpikeDetectorEA deinitialized. Reason: ", reason); +} +//+------------------------------------------------------------------+ +//| Expert tick function (not used for bar analysis in this EA) | +//+------------------------------------------------------------------+ +void OnTick() +{ + //--- + // We will use OnCalculate for bar-based analysis +} +//+------------------------------------------------------------------+ +//| Chart event function (not strictly needed for this EA) | +//+------------------------------------------------------------------+ +void OnChartEvent(const int id, + const long &lparam, + const double &dparam, + const string &sparam) +{ + //--- + // Can be used to handle chart events if necessary +} + +//+------------------------------------------------------------------+ +//| Expert new bar function / Indicator calculation event | +//+------------------------------------------------------------------+ +int OnCalculate(const int rates_total, // Size of the price[] arrays + const int prev_calculated, // Bars calculated at the previous call + const datetime &time[], // Time array + const double &open[], // Open price array + const double &high[], // High price array + const double &low[], // Low price array + const double &close[], // Close price array + const long &tick_volume[], // Tick Volume array + const long &volume[], // Real Volume array + const int &spread[]) // Spread array +{ + //--- Check for minimal rates_total + if (rates_total < 2) // Need at least one closed bar to compare + return (0); + + //--- Determine how many bars to calculate + int first_bar_to_calculate; + // On the very first call of OnCalculate (prev_calculated == 0) or if history was changed drastically + if (prev_calculated == 0 || prev_calculated > rates_total || rates_total - prev_calculated > InpLookbackForDrawing + 10) // Added a buffer for safety + { + first_bar_to_calculate = MathMax(0, rates_total - InpLookbackForDrawing); + Print("SpikeDetectorEA: First run or history refresh. Processing up to ", InpLookbackForDrawing, " bars from bar index ", first_bar_to_calculate); + // Clean up old arrows if doing a full refresh to avoid orphaned objects if bar times change + long chart_id = ChartID(); + for (int k = ObjectsTotal(chart_id, -1, OBJ_ARROW) - 1; k >= 0; k--) + { + string obj_name_check = ObjectName(chart_id, k, -1, OBJ_ARROW); + if (StringFind(obj_name_check, "SpikeArrow_Bearish_" + _Symbol + "_" + EnumToString((ENUM_TIMEFRAMES)Period())) == 0 || + StringFind(obj_name_check, "SpikeArrow_Bullish_" + _Symbol + "_" + EnumToString((ENUM_TIMEFRAMES)Period())) == 0) + { + ObjectDelete(chart_id, obj_name_check); + } + } + } + else + { + // prev_calculated is the number of bars calculated by the previous call. + // We want to start from one bar before the last one calculated by the previous call, + // to catch updates to the forming bar that has now closed, plus any new bars. + // rates_total - prev_calculated gives the number of new bars. + // We process (rates_total - prev_calculated) + 1 bars (the forming bar that closed + new bars) + // So, start from rates_total - (rates_total - prev_calculated + 1) = prev_calculated - 1 + first_bar_to_calculate = prev_calculated - 1; + } + // Ensure we don't go out of bounds (e.g. if prev_calculated was 0 due to error or very few bars) + first_bar_to_calculate = MathMax(0, first_bar_to_calculate); + // We don't want to process the current, still-forming bar (index rates_total - 1 usually) + // So loop up to rates_total - 1 (or < rates_total) + // If we are processing historical bars, i can go up to rates_total - 1 + // If we are processing only new bars, the last bar is rates_total - 1. + + //--- Main loop for processing bars + // We iterate up to rates_total-1 because the last bar (rates_total-1) is the current forming bar + // We are interested in closed bars. So, the last closed bar is at index rates_total-2 if rates_total > 1. + // However, if we start from prev_calculated-1, this logic handles it correctly. + // The loop should go up to `rates_total - 1` to process all available historical bars. + // The current bar (index rates_total - 1) will be processed on the next tick once it closes and a new bar forms. + for (int i = first_bar_to_calculate; i < rates_total; i++) + { + // Skip the current forming bar (bar 0 in terminal, index rates_total-1 in array if processing live) + // This logic is implicitly handled by how OnCalculate is typically called. + // The loop goes from an older bar up to the most recent *available* bar data. + // The last bar in the `time`, `open`, etc. arrays is `rates_total - 1`. + // If `i == rates_total - 1`, it's the current, potentially unclosed bar. + // We generally want to analyze closed bars. Let's adjust the loop to `i < rates_total -1` if we only want confirmed closed bars. + // However, for drawing on historical data, `i < rates_total` is fine. + // The key is that `prev_calculated` helps us only recalculate what's necessary. + // Let's assume we process up to the last available bar, and if it's the current forming bar, + // its spike status might change. The object deletion logic handles this. + + // If it's the very first bar available (index 0) and it has no history to form wicks properly, skip. + // This check might be too simplistic, but helps avoid issues with incomplete data at the start of history. + // if (i == 0 && rates_total < 3) continue; // This might be too restrictive. + + // Define object names for this bar to manage them + // Using time ensures uniqueness per bar. Adding symbol and timeframe for robustness. + string obj_time_str = TimeToString(time[i], TIME_DATE | TIME_MINUTES | TIME_SECONDS); // Added seconds for more uniqueness + string bearish_obj_name = StringFormat("SpikeArrow_Bearish_%s_%s_%s", _Symbol, EnumToString((ENUM_TIMEFRAMES)Period()), obj_time_str); + string bullish_obj_name = StringFormat("SpikeArrow_Bullish_%s_%s_%s", _Symbol, EnumToString((ENUM_TIMEFRAMES)Period()), obj_time_str); + + // Delete existing arrows for this bar before re-evaluating + // This allows settings changes to reflect immediately and removes arrows if a spike no longer qualifies + ObjectDelete(0, bearish_obj_name); + ObjectDelete(0, bullish_obj_name); + + // Get OHLC for the current bar i + double O = open[i]; + double H = high[i]; + double L = low[i]; + double C = close[i]; + + // Calculate candle components + double body_size = MathAbs(O - C); + double total_range = H - L; + double upper_wick = H - MathMax(O, C); + double lower_wick = MathMin(O, C) - L; + + // Avoid division by zero or issues with flat candles (doji, etc.) + if (total_range < _Point * 0.1) // If candle range is very small, skip (e.g. less than 0.1 points) + continue; + + bool is_bearish_spike = false; + bool is_bullish_spike = false; + double body_plus_epsilon = body_size + (_Point * 0.01); // Add a very small value related to point size + + // --- Bearish Spike Detection (Spike High) --- + // 1. Body is a small part of the total range + // 2. Upper wick is significantly larger than the body + // 3. Lower wick is small compared to the body (or upper wick) + if (body_size / total_range <= InpMaxBodyToTotalRangeRatio && + upper_wick / body_plus_epsilon >= InpMinWickToBodyRatio && + lower_wick / body_plus_epsilon <= InpMaxOppositeWickToBodyRatio && + upper_wick > lower_wick) // Ensure upper wick is dominant + { + is_bearish_spike = true; + } + + // --- Bullish Spike Detection (Spike Low) --- + // 1. Body is a small part of the total range + // 2. Lower wick is significantly larger than the body + // 3. Upper wick is small compared to the body (or lower wick) + if (body_size / total_range <= InpMaxBodyToTotalRangeRatio && + lower_wick / body_plus_epsilon >= InpMinWickToBodyRatio && + upper_wick / body_plus_epsilon <= InpMaxOppositeWickToBodyRatio && + lower_wick > upper_wick) // Ensure lower wick is dominant + { + is_bullish_spike = true; + } + + // --- Drawing --- + double arrow_offset_actual = InpArrowOffsetPoints * _Point; + + if (is_bearish_spike) + { + if (ObjectCreate(0, bearish_obj_name, OBJ_ARROW, 0, time[i], H + arrow_offset_actual)) + { + ObjectSetInteger(0, bearish_obj_name, OBJPROP_ARROWCODE, WINGDINGS_ARROW_DOWN); // Corrected + ObjectSetInteger(0, bearish_obj_name, OBJPROP_COLOR, InpBearishSpikeColor); + ObjectSetInteger(0, bearish_obj_name, OBJPROP_WIDTH, 1); + ObjectSetInteger(0, bearish_obj_name, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetString(0, bearish_obj_name, OBJPROP_TOOLTIP, "Bearish Spike"); + } + // else { PrintFormat("Failed to create bearish spike arrow for bar at %s. Error: %d", TimeToString(time[i]), GetLastError()); } + } + + if (is_bullish_spike) + { + if (ObjectCreate(0, bullish_obj_name, OBJ_ARROW, 0, time[i], L - arrow_offset_actual)) + { + ObjectSetInteger(0, bullish_obj_name, OBJPROP_ARROWCODE, WINGDINGS_ARROW_UP); // Corrected + ObjectSetInteger(0, bullish_obj_name, OBJPROP_COLOR, InpBullishSpikeColor); + ObjectSetInteger(0, bullish_obj_name, OBJPROP_WIDTH, 1); + ObjectSetInteger(0, bullish_obj_name, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetString(0, bullish_obj_name, OBJPROP_TOOLTIP, "Bullish Spike"); + } + // else { PrintFormat("Failed to create bullish spike arrow for bar at %s. Error: %d", TimeToString(time[i]), GetLastError()); } + } + } + //--- Return value of prev_calculated for next call + return (rates_total); +} +//+------------------------------------------------------------------+ diff --git a/Experts/xcatb.test.ea.mq5 b/Experts/xcatb.test.ea.mq5 index f7b8fb3f..4cf1c0d5 100644 --- a/Experts/xcatb.test.ea.mq5 +++ b/Experts/xcatb.test.ea.mq5 @@ -152,6 +152,14 @@ input bool eaReportSignals = true; // Report Signals input bool eaReportProtector = true; // Report Protector Actions input bool eaReportAfterTradesBalance = true; // Report Balance after a Trade Finished +// +// Collector ... +input group "Collector"; +input bool eaSaveTrades = false; // Save Trades +input bool eaSaveSignals = false; // Save Signals +input bool eaSaveWins = false; // Save Winning Conditions +input bool eaSaveLosts = false; // Save Lost Conditions + // // Variables ... @@ -446,7 +454,14 @@ void OnTick() eaSignal.targets, eaConditions.targets // ); - eaSignal.conditions = eaCatbConditions.GenerateSummary(); + eaSignal.conditions = + eaCatbConditions + .GenerateSummary( + false, // Commons ... + true, // Conditions ... + true, // Scores ... + false // Ignore Falses ... + ); // // Execute Signal ... @@ -796,13 +811,18 @@ void InitRequirements() eaTradeManager = new XCXCATBEATradeManager( eaTrade // ); - eaTradeManager.SaveTrades(true); + // eaTradeManager.SetAlertPrefix(eaTag); eaTradeManager.SetAlertLogAlerts(eaLogAlerts); eaTradeManager.SetAlertMailAlerts(eaMailAlerts); eaTradeManager.SetAlertPushAlerts(eaPushAlerts); eaTradeManager.SetAlertEnableAlerts(eaEnableAlerts); eaTradeManager.SetAlertTerminalAlerts(eaTerminalAlerts); + // + eaTradeManager.SaveWins(eaSaveWins); + eaTradeManager.SaveLosts(eaSaveLosts); + eaTradeManager.SaveTrades(eaSaveTrades); + eaTradeManager.SaveSignals(eaSaveSignals); // // Setting Trade Reports ... diff --git a/Libraries/x-saherelm.x-trade.lib.mq5 b/Libraries/x-saherelm.x-trade.lib.mq5 index 2256bd08..d36f8748 100644 --- a/Libraries/x-saherelm.x-trade.lib.mq5 +++ b/Libraries/x-saherelm.x-trade.lib.mq5 @@ -1665,6 +1665,32 @@ struct XSignal // return result; } + + // + string GetFileName() + { + // + string result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + result = + // + symbol + "\\" + + ToString(type) + "\\" + + ToString(period) + "_" + + ToFormatString(time) + // + ; + + // + return result; + } }; // diff --git a/XCATBEA/Classes/xcatbea.signaller.class.mq5 b/XCATBEA/Classes/xcatbea.signaller.class.mq5 new file mode 100644 index 00000000..41bf4214 --- /dev/null +++ b/XCATBEA/Classes/xcatbea.signaller.class.mq5 @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 Indicator +// ------------------------------------------------- +// Name: XCXCATBEASignaller ... +// Description: Class for XCATBEA Signaller ... +// +// +// Maintainer: +// ------------ +// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) +// +////////////////////////////////////////////////////// +// +// Global Properties ... +#property library +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://saherelm.ir" +#property version "1.00" +#property description "SaherElm Class for XCATBEA Signaller" +#property strict + +// +// Imports ... +#include "../Libraries/xcatbea.lib.mq5"; +#include "../Libraries/xcatbea.signaller.lib.mq5"; + +// +// Definitions ... +class XCXCATBEASignaller : XCBase +{ +}; \ No newline at end of file diff --git a/XCATBEA/Classes/xcatbea.x-data.collector.class.mq5 b/XCATBEA/Classes/xcatbea.x-data.collector.class.mq5 index 57d02091..d16da67b 100644 --- a/XCATBEA/Classes/xcatbea.x-data.collector.class.mq5 +++ b/XCATBEA/Classes/xcatbea.x-data.collector.class.mq5 @@ -114,10 +114,10 @@ class XCXCATBEADataCollector : public XCBase /** * Save Specified Content into Specified File Name ... - * + * * @param fileName: string, file name ... * @param content: string, content ... - * + * * @return ( bool ) */ bool Save( @@ -153,6 +153,48 @@ class XCXCATBEADataCollector : public XCBase return result; } + /** + * Append Specified Content into Specified File Name ... + * + * @param fileName: string, file name ... + * @param content: string, content ... + * + * @return ( bool ) + */ + bool Append( + string fileName, + string content // + ) + { + // + bool result = false; + + // + result = IsValid(content); + if (!result) + { + return result; + } + + // + // + int mHandler = GetFileHandlerForWrite(fileName); + result = mHandler != INVALID_HANDLE; + if (!result) + { + return result; + } + + // + FileSeek(mHandler, 0, SEEK_END); + FileWrite(mHandler, content); + FileFlush(mHandler); + FileClose(mHandler); + + // + return result; + } + // // Protected ... protected: diff --git a/XCATBEA/Classes/xcatbea.x-trade.manager.class.mq5 b/XCATBEA/Classes/xcatbea.x-trade.manager.class.mq5 index 0550e5b3..2b3d3209 100644 --- a/XCATBEA/Classes/xcatbea.x-trade.manager.class.mq5 +++ b/XCATBEA/Classes/xcatbea.x-trade.manager.class.mq5 @@ -459,15 +459,15 @@ struct XCATBEATrade // result = // - ToString("Swap: ", swap) + - ToString("Profit: ", profit) + - ToString("Commission: ", commission) + - ToString("Finalize: ", ToString(finalize)) + - ToString("PositionID: ", positionID) + + ToString("Swap", swap) + + ToString("Profit", profit) + + ToString("Commission", commission) + + ToString("Finalize", ToString(finalize)) + + ToString("PositionID", positionID) + ToString("----------------") + "\n" + - ToString("Signaled At: ", ToFormatString(signaledAt)) + - ToString("Executed At: ", ToFormatString(executedAt)) + - ToString("Finalized At: ", ToFormatString(finalizedAt)) + ToString("Signaled At", ToFormatString(signaledAt)) + + ToString("Executed At", ToFormatString(executedAt)) + + ToString("Finalized At", ToFormatString(finalizedAt)) // ; @@ -581,6 +581,111 @@ struct XCATBEATrade // return result; } + + string GetConditionsString() + { + // + string result = NULL; + + // + double bullishScore = 0; + double bearishScore = 0; + conditions + .conditions + .GenerateScore( + bullishScore, + bearishScore // + ); + + // + string conditionsStr = + conditions + .conditions + .GenerateSummary( + false, + true, // Only Conditions ... + false, + false // Ignore False Conditions ... + ); + string condParts[]; + int condPartsCount = SplitContent( + condParts, + conditionsStr, + "\n" // + ); + if (IsValidSize(condPartsCount)) + { + // + conditionsStr = NULL; + while (HasChild(condParts)) + { + // + string iStr = condParts[0]; + ArrayRemove( + condParts, + 0, + 1 // + ); + + // + bool isLast = !HasChild(condParts); + + // + bool isCond = Contains( + "is", + iStr, + false // + ); + if (isCond) + { + // + string iParts[]; + int iPartsCount = SplitContent( + iParts, + iStr, + ":" // + ); + if (iPartsCount == 2) + { + // + string iVal = iParts[1]; + StringTrimLeft(iVal); + StringTrimRight(iVal); + bool isPassed = ToBoolean(iVal); + conditionsStr += (isPassed ? "" : "!") + "conditions." + iParts[0] + (!isLast ? " &&" : "") + "\n"; + } + } + } + Clean(condParts); + } + else + { + conditionsStr = NULL; + } + + // + result = + "(" + "\n" + + "\\" + "\\" + " " + ToString("Type", ToString(signal.type)) + + "\\" + "\\" + " " + ToString("Profit", profit) + + "\\" + "\\" + " " + ToString("Finalize", ToString(finalize)) + + "\\" + "\\" + " " + ToString("PositionID", positionID) + + "\\" + "\\" + " " + ToString("----------------") + "\n" + + "\\" + "\\" + " " + ToString("Signaled At", ToFormatString(signaledAt)) + + "\\" + "\\" + " " + ToString("Executed At", ToFormatString(executedAt)) + + "\\" + "\\" + " " + ToString("Finalized At", ToFormatString(finalizedAt)) + + "\\" + "\\" + " " + ToString("----------------") + "\n" + + "\\" + "\\" + " " + ToString("Scores:") + "\n" + + "\\" + "\\" + " " + ToString("Bullish", bullishScore) + + "\\" + "\\" + " " + ToString("Bearish", bearishScore) + + "\\" + "\\" + " " + ToString("----------------") + "\n" + + conditionsStr + + (IsValid(conditionsStr) ? "" : "") + + ") && "; + + // + return result; + } }; // @@ -677,6 +782,42 @@ class XCXCATBEATradeManager : public XCBaseAlert mSaveTrades = value; } + // + bool SaveSignals() + { + return mSaveSignals; + } + + // + void SaveSignals(bool value) + { + mSaveSignals = value; + } + + // + bool SaveWins() + { + return mSaveWins; + } + + // + void SaveWins(bool value) + { + mSaveWins = value; + } + + // + bool SaveLosts() + { + return mSaveLosts; + } + + // + void SaveLosts(bool value) + { + mSaveLosts = value; + } + // void SetForceCloseTradesAt( string _forceCloseAt // @@ -1191,6 +1332,9 @@ class XCXCATBEATradeManager : public XCBaseAlert SendAlert(message); } + // + SaveSignal(signal); + // // Check Trade Permission ... if (!allowTrade) @@ -1480,7 +1624,8 @@ class XCXCATBEATradeManager : public XCBaseAlert // // Save Trade ... - Save(trades[idx]); + SaveWin(trades[idx]); + SaveTrade(trades[idx]); // // Handle Balance Reporting ... @@ -1533,7 +1678,7 @@ class XCXCATBEATradeManager : public XCBaseAlert // // Save Trade ... - Save(trades[idx]); + SaveTrade(trades[idx]); // // Handle Balance Reporting ... @@ -1545,6 +1690,14 @@ class XCXCATBEATradeManager : public XCBaseAlert // bool isInProfit = trades[idx].profit > 0; + if (isInProfit) + { + SaveWin(trades[idx]); + } + else + { + SaveLost(trades[idx]); + } // XCATBEASymbolInfo iSymbolInfo; @@ -1605,7 +1758,15 @@ class XCXCATBEATradeManager : public XCBaseAlert // // Save Trade ... - Save(trades[idx]); + SaveTrade(trades[idx]); + if (isInProfit) + { + SaveWin(trades[idx]); + } + else + { + SaveLost(trades[idx]); + } // // Handle Balance Reporting ... @@ -1863,17 +2024,17 @@ class XCXCATBEATradeManager : public XCBaseAlert // Tools ... // - string GetFilePath(XCATBEATrade &trade) + string GetTradeFilePath(XCATBEATrade &trade) { // string fileName = trade.GetFileName(); // - return GetFilePath(fileName); + return GetTradeFilePath(fileName); } // - string GetFilePath(string fileName) + string GetTradeFilePath(string fileName) { // string result = NULL; @@ -1892,6 +2053,72 @@ class XCXCATBEATradeManager : public XCBaseAlert return result; } + // + string GetSignalFilePath(string fileName) + { + // + string result = NULL; + + // + string mPath = collector.Path(); + + // + result = + // + mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log" + // + ; + + // + return result; + } + + // + string GetWinsFilePath(XCATBEATrade &trade) + { + // + string result = NULL; + + // + string mPath = collector.Path(); + + // + result = + // + mPath + "\\" + + "Conditions" + "\\" + + trade.signal.symbol + "\\" + + ToString(trade.signal.period) + "_wins.x121.log" + // + ; + + // + return result; + } + + // + string GetLostsFilePath(XCATBEATrade &trade) + { + // + string result = NULL; + + // + string mPath = collector.Path(); + + // + result = + // + mPath + "\\" + + "Conditions" + "\\" + + trade.signal.symbol + "\\" + + ToString(trade.signal.period) + "_losts.x121.log" + // + ; + + // + return result; + } + // void HandleReportBalance(string prefix = NULL) { @@ -1928,7 +2155,8 @@ class XCXCATBEATradeManager : public XCBaseAlert } // - bool Save( + // Save Trades ... + bool SaveTrade( XCATBEATrade &trade, bool includeSummary = true, bool includeSignal = true, @@ -1945,7 +2173,7 @@ class XCXCATBEATradeManager : public XCBaseAlert } // - string filePath = GetFilePath(trade); + string filePath = GetTradeFilePath(trade); result = IsValid(filePath); if (!result) { @@ -1969,6 +2197,139 @@ class XCXCATBEATradeManager : public XCBaseAlert return result; } + // + // Save Signals ... + bool SaveSignal(XSignal &signal) + { + // + bool result = false; + + // + // Check Signal Save is Enabled ... + if (!mSaveSignals) + { + return result; + } + + // + // Check Signal Validation ... + if (!signal.IsValid()) + { + return result; + } + + // + // Retrieve Signal File Name ... + string signalFileName = signal.GetFileName(); + result = IsValid(signalFileName); + if (!result) + { + return result; + } + + // + // Retriev and Validate File Name ... + string filePath = GetSignalFilePath(signalFileName); + result = IsValid(filePath); + if (!result) + { + return result; + } + + // + // Generate Content ... + string content = ""; + content += + ToString("----------------") + "\n" + + ToString("Signal: ") + "\n" + + ToString("----------------") + "\n" + + signal.ToString() + "\n"; + content += + ToString("----------------") + "\n" + + ToString("Conditions: ") + "\n" + + ToString("----------------") + "\n" + + signal.conditions + "\n"; + + // + // Save Content to File ... + result = collector.Save( + filePath, + content // + ); + + // + // Return Result ... + return result; + } + + // + bool SaveWin(XCATBEATrade &trade) + { + // + bool result = false; + + // + if (!mSaveWins) + { + return result; + } + + // + string filePath = GetWinsFilePath(trade); + result = IsValid(filePath); + if (!result) + { + return result; + } + + // + // Preparing Conditions Content ... + string content = trade.GetConditionsString(); + + // + result = collector.Append( + filePath, + content // + ); + + // + return result; + } + + // + bool SaveLost(XCATBEATrade &trade) + { + // + bool result = false; + + // + if (!mSaveLosts) + { + return result; + } + + // + string filePath = GetLostsFilePath(trade); + result = IsValid(filePath); + if (!result) + { + return result; + } + + // + // Preparing Conditions Content ... + string content = trade.GetConditionsString(); + + // + result = collector.Append( + filePath, + content // + ); + + // + return result; + } + // // Symbol Info ... @@ -2277,7 +2638,10 @@ class XCXCATBEATradeManager : public XCBaseAlert ENUM_XCATBEA_TRADE_RESTRICTION_PERIOD restrictionsPeriod; // + bool mSaveWins; + bool mSaveLosts; bool mSaveTrades; + bool mSaveSignals; // bool reportTrades;