| //+------------------------------------------------------------------+
|
| //| PaperTrading.mqh |
|
| //| Copyright 2025, EscapeEA |
|
| //| https://www.escapeea.com |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright 2025, EscapeEA"
|
| #property link "https://www.escapeea.com"
|
| #property version "2.00"
|
| #property strict
|
|
|
| #include <Trade\PositionInfo.mqh>
|
| #include <Trade\DealInfo.mqh>
|
| #include <Trade\AccountInfo.mqh>
|
| #include <Trade\SymbolInfo.mqh>
|
| #include <Arrays\ArrayObj.mqh>
|
|
|
| // Forward declarations
|
| class CRiskManager; // Forward declaration of RiskManager
|
|
|
| // Include the actual header files after forward declarations
|
| #include "RiskManager.mqh"
|
|
|
| //+------------------------------------------------------------------+
|
| //| Paper Trade Settings |
|
| //+------------------------------------------------------------------+
|
| input group "=== Paper Trading Settings ==="
|
| input bool InpEnablePaperTrading = true; // Enable Paper Trading
|
| input int InpPaperTradesRequired = 5; // Total Paper Trades
|
| input int InpPaperWinsRequired = 3; // Required Wins for Live Trading
|
| input double InpPaperInitialBalance = 10000.0; // Initial Paper Balance
|
| input bool InpSimulateSlippage = true; // Simulate Slippage
|
| input bool InpSimulatePartialFills = true; // Simulate Partial Fills
|
| input double InpMaxSlippagePips = 1.0; // Max Slippage (pips)
|
| input int InpFillProbability = 90; // Fill Probability (%)
|
|
|
| //+------------------------------------------------------------------+
|
| //| Paper Trade Structure |
|
| //+------------------------------------------------------------------+
|
| struct SPaperTrade {
|
| ulong ticket; // Trade ticket
|
| string symbol; // Symbol
|
| ENUM_ORDER_TYPE type; // Order type
|
| double volume; // Trade volume
|
| double openPrice; // Open price
|
| double stopLoss; // Stop loss
|
| double takeProfit; // Take profit
|
| datetime openTime; // Open time
|
| datetime closeTime; // Close time
|
| double closePrice; // Close price
|
| double commission; // Commission
|
| double swap; // Swap
|
| double profit; // Profit/loss
|
| bool isClosed; // Is trade closed
|
| bool isWinner; // Is winning trade
|
|
|
| // Constructor
|
| SPaperTrade() {
|
| ticket = 0;
|
| volume = 0.0;
|
| openPrice = 0.0;
|
| stopLoss = 0.0;
|
| takeProfit = 0.0;
|
| closePrice = 0.0;
|
| commission = 0.0;
|
| swap = 0.0;
|
| profit = 0.0;
|
| isClosed = false;
|
| isWinner = false;
|
| }
|
| };
|
|
|
| //+------------------------------------------------------------------+
|
| //| CPaperTrading class |
|
| //+------------------------------------------------------------------+
|
| class CPaperTrading
|
| {
|
| private:
|
| // Trade management
|
| CArrayObj m_trades; // Array of paper trades
|
| double m_balance; // Current paper balance
|
| double m_equity; // Current paper equity
|
| int m_totalTrades; // Total paper trades
|
| int m_winningTrades; // Number of winning trades
|
| int m_losingTrades; // Number of losing trades
|
| bool m_liveTrading; // Live trading enabled
|
|
|
| // Simulation parameters
|
| double m_slippage; // Current slippage
|
| double m_spread; // Current spread
|
|
|
| // External components
|
| CSymbolInfo m_symbol; // Symbol info
|
| CAccountInfo m_account; // Account info
|
| CRiskManager *m_riskManager; // Risk manager (forward declared)
|
|
|
| // Private methods
|
| double CalculateCommission(double volume, double price);
|
| double CalculateSwap(double volume, double price, ENUM_ORDER_TYPE type);
|
| double CalculateSlippage();
|
| bool SimulateFill();
|
| void UpdateEquity();
|
|
|
| public:
|
| // Constructor/Destructor
|
| CPaperTrading(CRiskManager *riskManager = NULL);
|
| ~CPaperTrading();
|
|
|
| // Initialization
|
| bool Initialize(CSymbolInfo *symbol, CAccountInfo *account,
|
| CEnhancedRiskManager *riskManager);
|
| void Deinitialize();
|
|
|
| // Trading operations
|
| bool OpenPosition(ENUM_ORDER_TYPE type, double volume,
|
| double price, double sl, double tp,
|
| string comment = "");
|
| bool ClosePosition(ulong ticket, double price);
|
| void CheckForExits();
|
|
|
| // Getters
|
| double GetBalance() const { return m_balance; }
|
| double GetEquity() const { return m_equity; }
|
| int GetTotalTrades() const { return m_totalTrades; }
|
| int GetWinningTrades() const { return m_winningTrades; }
|
| int GetLosingTrades() const { return m_losingTrades; }
|
| double GetWinRate() const {
|
| return (m_totalTrades > 0) ? (double)m_winningTrades / m_totalTrades * 100.0 : 0.0;
|
| }
|
| bool IsLiveTradingAllowed() const {
|
| return m_liveTrading || (!InpEnablePaperTrading);
|
| }
|
|
|
| // State management
|
| void Reset();
|
| void Update();
|
|
|
| // Chart display
|
| void UpdateChart();
|
| };
|
|
|
| //+------------------------------------------------------------------+
|
| //| Constructor |
|
| //+------------------------------------------------------------------+
|
| CPaperTrading::CPaperTrading() : m_balance(InpPaperInitialBalance),
|
| m_equity(InpPaperInitialBalance),
|
| m_totalTrades(0),
|
| m_winningTrades(0),
|
| m_losingTrades(0),
|
| m_liveTrading(false),
|
| m_slippage(0.0),
|
| m_spread(0.0),
|
| m_symbol(NULL),
|
| m_account(NULL),
|
| m_riskManager(NULL)
|
| {
|
| m_trades.Clear();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CPaperTrading::~CPaperTrading()
|
| {
|
| Deinitialize();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Initialize paper trading system |
|
| //+------------------------------------------------------------------+
|
| bool CPaperTrading::Initialize(CSymbolInfo *symbol, CAccountInfo *account,
|
| CEnhancedRiskManager *riskManager)
|
| {
|
| // Validate inputs
|
| if(symbol == NULL || account == NULL || riskManager == NULL) {
|
| Print("PaperTrading: Invalid initialization parameters");
|
| return false;
|
| }
|
|
|
| // Store references
|
| m_symbol = symbol;
|
| m_account = account;
|
| m_riskManager = riskManager;
|
|
|
| // Reset state
|
| Reset();
|
|
|
| // Initialize chart objects
|
| UpdateChart();
|
|
|
| return true;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Clean up resources |
|
| //+------------------------------------------------------------------+
|
| void CPaperTrading::Deinitialize()
|
| {
|
| // Clean up resources
|
| m_trades.Clear();
|
|
|
| // Remove chart objects
|
| string prefix = "PaperTrade_";
|
| ObjectsDeleteAll(0, prefix);
|
| ChartRedraw();
|
|
|
| // Reset references
|
| m_symbol = NULL;
|
| m_account = NULL;
|
| m_riskManager = NULL;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Open a new paper position |
|
| //+------------------------------------------------------------------+
|
| bool CPaperTrading::OpenPosition(ENUM_ORDER_TYPE type, double volume,
|
| double price, double sl, double tp,
|
| string comment = "")
|
| {
|
| // Check if paper trading is enabled
|
| if(!InpEnablePaperTrading) {
|
| m_liveTrading = true;
|
| return true; // Allow live trading
|
| }
|
|
|
| // Check if we have enough wins to trade live
|
| if(m_winningTrades >= InpPaperWinsRequired && m_totalTrades >= InpPaperTradesRequired) {
|
| m_liveTrading = true;
|
| return true; // Allow live trading
|
| }
|
|
|
| // Check if we've reached the maximum number of paper trades
|
| if(m_totalTrades >= InpPaperTradesRequired) {
|
| Print("PaperTrading: Maximum number of paper trades reached (", InpPaperTradesRequired, "). ",
|
| m_winningTrades, " wins out of ", InpPaperWinsRequired, " required for live trading.");
|
| return false;
|
| }
|
|
|
| // Check risk management
|
| if(m_riskManager != NULL && !m_riskManager.IsTradingAllowed()) {
|
| Print("PaperTrading: Risk management restricts trading");
|
| return false;
|
| }
|
|
|
| // Check if we can simulate a fill
|
| if(!SimulateFill()) {
|
| Print("PaperTrading: Order fill simulation failed");
|
| return false;
|
| }
|
|
|
| // Calculate slippage if enabled
|
| double actualPrice = price;
|
| if(InpSimulateSlippage) {
|
| double slippage = CalculateSlippage();
|
| if(type == ORDER_TYPE_BUY || type == ORDER_TYPE_BUY_LIMIT || type == ORDER_TYPE_BUY_STOP) {
|
| actualPrice += slippage;
|
| } else {
|
| actualPrice -= slippage;
|
| }
|
| }
|
|
|
| // Create new trade
|
| SPaperTrade *trade = new SPaperTrade();
|
| if(trade == NULL) {
|
| Print("PaperTrading: Failed to create new trade");
|
| return false;
|
| }
|
|
|
| // Initialize trade
|
| trade.ticket = (ulong)(TimeCurrent() % 1000000 + m_trades.Total());
|
| trade.symbol = m_symbol.Name();
|
| trade.type = type;
|
| trade.volume = volume;
|
| trade.openPrice = actualPrice;
|
| trade.stopLoss = sl;
|
| trade.takeProfit = tp;
|
| trade.openTime = TimeCurrent();
|
| trade.commission = CalculateCommission(volume, actualPrice);
|
| trade.swap = 0.0; // Will be updated on bar close
|
| trade.isClosed = false;
|
| trade.isWinner = false;
|
|
|
| // Add to trades array
|
| if(!m_trades.Add(trade)) {
|
| delete trade;
|
| Print("PaperTrading: Failed to add trade to array");
|
| return false;
|
| }
|
|
|
| // Update statistics
|
| m_totalTrades++;
|
|
|
| // Update balance (subtract margin, will be added back on close)
|
| double margin = m_riskManager != NULL ?
|
| m_riskManager.CalculateMargin(trade.symbol, trade.volume, trade.type) :
|
| (trade.volume * m_symbol.TickValue() * m_symbol.TickSize());
|
| m_balance -= margin;
|
|
|
| PrintFormat("PaperTrading: Opened %s position %.2f lots at %.5f (SL: %.5f, TP: %.5f)",
|
| EnumToString(type), volume, actualPrice, sl, tp);
|
|
|
| // Update chart
|
| UpdateChart();
|
|
|
| return true;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Close a paper position |
|
| //+------------------------------------------------------------------+
|
| bool CPaperTrading::ClosePosition(ulong ticket, double price)
|
| {
|
| for(int i = 0; i < m_trades.Total(); i++) {
|
| SPaperTrade *trade = m_trades.At(i);
|
| if(trade == NULL || trade.isClosed || trade.ticket != ticket) continue;
|
|
|
| // Calculate P/L
|
| double priceDiff = price - trade.openPrice;
|
| if(trade.type == ORDER_TYPE_SELL || trade.type == ORDER_TYPE_SELL_LIMIT || trade.type == ORDER_TYPE_SELL_STOP) {
|
| priceDiff = -priceDiff;
|
| }
|
|
|
| double pips = priceDiff / m_symbol.Point();
|
| double profit = pips * m_symbol.TickValue() * trade.volume / (m_symbol.Point() / m_symbol.TickSize());
|
|
|
| // Update trade info
|
| trade.closePrice = price;
|
| trade.closeTime = TimeCurrent();
|
| trade.profit = profit;
|
| trade.isClosed = true;
|
|
|
| // Update balance
|
| m_balance += profit - trade.commission - trade.swap;
|
|
|
| // Update statistics
|
| if(profit > 0) {
|
| m_winningTrades++;
|
| } else {
|
| m_losingTrades++;
|
| }
|
|
|
| // Update equity
|
| UpdateEquity();
|
|
|
| PrintFormat("PaperTrading: Closed position %d at %.5f, P/L: $%.2f",
|
| ticket, price, profit - trade.commission - trade.swap);
|
|
|
| // Update chart
|
| UpdateChart();
|
|
|
| return true;
|
| }
|
|
|
| return false;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Check for exit conditions |
|
| //+------------------------------------------------------------------+
|
| void CPaperTrading::CheckForExits()
|
| {
|
| double bid = m_symbol.Bid();
|
| double ask = m_symbol.Ask();
|
|
|
| for(int i = m_trades.Total() - 1; i >= 0; i--) {
|
| SPaperTrade *trade = m_trades.At(i);
|
| if(trade == NULL || trade.isClosed) continue;
|
|
|
| // Check if position should be closed by stop loss or take profit
|
| double currentPrice = (trade.type == ORDER_TYPE_BUY || trade.type == ORDER_TYPE_BUY_LIMIT || trade.type == ORDER_TYPE_BUY_STOP) ? bid : ask;
|
|
|
| // Check stop loss
|
| if((trade.type == ORDER_TYPE_BUY || trade.type == ORDER_TYPE_BUY_LIMIT || trade.type == ORDER_TYPE_BUY_STOP) &&
|
| trade.stopLoss > 0 && currentPrice <= trade.stopLoss) {
|
| ClosePosition(trade.ticket, trade.stopLoss);
|
| trade.isWinner = false;
|
| continue;
|
| }
|
|
|
| if((trade.type == ORDER_TYPE_SELL || trade.type == ORDER_TYPE_SELL_LIMIT || trade.type == ORDER_TYPE_SELL_STOP) &&
|
| trade.stopLoss > 0 && currentPrice >= trade.stopLoss) {
|
| ClosePosition(trade.ticket, trade.stopLoss);
|
| trade.isWinner = false;
|
| continue;
|
| }
|
|
|
| // Check take profit
|
| if((trade.type == ORDER_TYPE_BUY || trade.type == ORDER_TYPE_BUY_LIMIT || trade.type == ORDER_TYPE_BUY_STOP) &&
|
| trade.takeProfit > 0 && currentPrice >= trade.takeProfit) {
|
| ClosePosition(trade.ticket, trade.takeProfit);
|
| trade.isWinner = true;
|
| continue;
|
| }
|
|
|
| if((trade.type == ORDER_TYPE_SELL || trade.type == ORDER_TYPE_SELL_LIMIT || trade.type == ORDER_TYPE_SELL_STOP) &&
|
| trade.takeProfit > 0 && currentPrice <= trade.takeProfit) {
|
| ClosePosition(trade.ticket, trade.takeProfit);
|
| trade.isWinner = true;
|
| continue;
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Calculate commission for a trade |
|
| //+------------------------------------------------------------------+
|
| double CPaperTrading::CalculateCommission(double volume, double price)
|
| {
|
| // Default commission: $5 per lot
|
| return 5.0 * volume;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Calculate swap for an open position |
|
| //+------------------------------------------------------------------+
|
| double CPaperTrading::CalculateSwap(double volume, double price, ENUM_ORDER_TYPE type)
|
| {
|
| // Simulate swap based on position type and size
|
| double swapRate = (type == ORDER_TYPE_BUY) ? -0.5 : 0.2; // pips per lot per day
|
| return volume * swapRate * m_symbol.Point() * 10000.0;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Calculate slippage for order execution |
|
| //+------------------------------------------------------------------
|
| double CPaperTrading::CalculateSlippage()
|
| {
|
| if(!InpSimulateSlippage) return 0.0;
|
|
|
| // Random slippage up to max slippage
|
| return MathRand() / 32767.0 * InpMaxSlippagePips * m_symbol.Point();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Simulate order fill probability |
|
| //+------------------------------------------------------------------+
|
| bool CPaperTrading::SimulateFill()
|
| {
|
| if(!InpSimulatePartialFills) return true;
|
|
|
| // Random chance of fill based on fill probability
|
| return (MathRand() % 100) < InpFillProbability;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Update equity based on open positions |
|
| //+------------------------------------------------------------------+
|
| void CPaperTrading::UpdateEquity()
|
| {
|
| double floatingPL = 0.0;
|
| double bid = m_symbol.Bid();
|
| double ask = m_symbol.Ask();
|
| double pointValue = m_symbol.Point() * m_symbol.TradeTickValue() / m_symbol.TradeTickSize();
|
|
|
| for(int i = 0; i < m_trades.Total(); i++) {
|
| SPaperTrade *trade = m_trades.At(i);
|
| if(trade == NULL || trade.isClosed) continue;
|
|
|
| if(trade.type == ORDER_TYPE_BUY) {
|
| floatingPL += (bid - trade.openPrice) * trade.volume * pointValue;
|
| } else {
|
| floatingPL += (trade.openPrice - ask) * trade.volume * pointValue;
|
| }
|
| }
|
|
|
| m_equity = m_balance + floatingPL;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Reset paper trading account |
|
| //+------------------------------------------------------------------+
|
| void CPaperTrading::Reset()
|
| {
|
| // Clear existing trades
|
| m_trades.Clear();
|
|
|
| // Reset metrics
|
| m_balance = InpPaperInitialBalance;
|
| m_equity = InpPaperInitialBalance;
|
| m_totalTrades = 0;
|
| m_winningTrades = 0;
|
| m_losingTrades = 0;
|
| m_liveTrading = false;
|
|
|
| // Reset chart
|
| UpdateChart();
|
|
|
| Print("PaperTrading: Account reset");
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Update paper trading system |
|
| //+------------------------------------------------------------------+
|
| void CPaperTrading::Update()
|
| {
|
| // Update market data
|
| m_symbol.RefreshRates();
|
|
|
| // Check for exits
|
| CheckForExits();
|
|
|
| // Update equity
|
| UpdateEquity();
|
|
|
| // Update chart periodically
|
| static datetime lastUpdate = 0;
|
| if(TimeCurrent() - lastUpdate >= 1) {
|
| UpdateChart();
|
| lastUpdate = TimeCurrent();
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Update chart with paper trading information |
|
| //+------------------------------------------------------------------+
|
| void CPaperTrading::UpdateChart()
|
| {
|
| string prefix = "PaperTrade_";
|
|
|
| // Create or update info panel
|
| string objName = prefix + "Info";
|
|
|
| // Create or update info text
|
| string infoText = StringFormat("=== Paper Trading ===\n" +
|
| "Balance: $%.2f\n" +
|
| "Equity: $%.2f\n" +
|
| "Trades: %d (%d wins, %d losses)\n" +
|
| "Win Rate: %.1f%%\n" +
|
| "Live Trading: %s",
|
| m_balance, m_equity,
|
| m_totalTrades, m_winningTrades, m_losingTrades,
|
| GetWinRate(),
|
| m_liveTrading ? "Enabled" : "Disabled");
|
|
|
| // Create or update the info label
|
| if(ObjectFind(0, objName) < 0) {
|
| ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
|
| ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
| ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 20);
|
| ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 20);
|
| ObjectSetInteger(0, objName, OBJPROP_COLOR, clrWhite);
|
| ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrNavy);
|
| ObjectSetInteger(0, objName, OBJPROP_BACK, false);
|
| ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
|
| ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
|
| ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0);
|
| }
|
|
|
| ObjectSetString(0, objName, OBJPROP_TEXT, infoText);
|
| ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 8);
|
|
|
| // Update trade markers on chart
|
| for(int i = 0; i < m_trades.Total(); i++) {
|
| SPaperTrade *trade = m_trades.At(i);
|
| if(trade == NULL) continue;
|
|
|
| string tradePrefix = prefix + IntegerToString(trade.ticket) + "_";
|
|
|
| // Create or update entry marker
|
| string entryName = tradePrefix + "Entry";
|
| if(ObjectFind(0, entryName) < 0) {
|
| ObjectCreate(0, entryName, OBJ_ARROW, 0, trade.openTime,
|
| trade.type <= 1 ? trade.openPrice + 10 * m_symbol.Point() : trade.openPrice - 10 * m_symbol.Point());
|
| ObjectSetInteger(0, entryName, OBJPROP_ARROWCODE, trade.type <= 1 ? 233 : 234);
|
| ObjectSetInteger(0, entryName, OBJPROP_COLOR, trade.type <= 1 ? clrLime : clrRed);
|
| ObjectSetInteger(0, entryName, OBJPROP_WIDTH, 2);
|
| }
|
|
|
| // Create or update exit marker if closed
|
| if(trade.isClosed) {
|
| string exitName = tradePrefix + "Exit";
|
| if(ObjectFind(0, exitName) < 0) {
|
| ObjectCreate(0, exitName, OBJ_ARROW, 0, trade.closeTime,
|
| trade.type <= 1 ? trade.closePrice - 10 * m_symbol.Point() : trade.closePrice + 10 * m_symbol.Point());
|
| ObjectSetInteger(0, exitName, OBJPROP_ARROWCODE, 251);
|
| ObjectSetInteger(0, exitName, OBJPROP_COLOR, trade.isWinner ? clrLime : clrRed);
|
| ObjectSetInteger(0, exitName, OBJPROP_WIDTH, 2);
|
| }
|
| }
|
| }
|
|
|
| // Redraw the chart
|
| ChartRedraw();
|
| if(ObjectFind(0, objName) < 0) {
|
| ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
|
| ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 10);
|
| ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 10);
|
| ObjectSetInteger(0, objName, OBJPROP_XSIZE, 200);
|
| ObjectSetInteger(0, objName, OBJPROP_YSIZE, 120);
|
| ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrWhiteSmoke);
|
| ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
|
| ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
| ObjectSetInteger(0, objName, OBJPROP_BACK, false);
|
| }
|
|
|
| // Update info text
|
| string infoText = "=== Paper Trading ===\n";
|
| infoText += StringFormat("Balance: %.2f %s\n", m_balance, m_account.Currency());
|
| infoText += StringFormat("Equity: %.2f %s\n", m_equity, m_account.Currency());
|
| infoText += StringFormat("Trades: %d (%dW/%dL)\n",
|
| m_totalTrades, m_winningTrades, m_losingTrades);
|
| infoText += StringFormat("Win Rate: %.1f%%\n", GetWinRate());
|
| infoText += StringFormat("Status: %s",
|
| m_liveTrading ? "LIVE TRADING" : "PAPER TRADING");
|
|
|
| objName = prefix + "InfoText";
|
| if(ObjectFind(0, objName) < 0) {
|
| ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
|
| ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 15);
|
| ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 15);
|
| ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
| ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBlack);
|
| ObjectSetString(0, objName, OBJPROP_FONT, "Arial");
|
| ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 8);
|
| }
|
| ObjectSetString(0, objName, OBJPROP_TEXT, infoText);
|
|
|
| // Redraw chart
|
| ChartRedraw();
|
| }
|
| //+------------------------------------------------------------------+
|