forked from Princeec13/mql5
588 lines
21 KiB
MQL5
588 lines
21 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| PaperTrading.mqh |
|
||
|
//| Copyright 2023, MetaQuotes Software Corp. |
|
||
|
//| https://www.mql5.com |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property copyright "Copyright 2023, MetaQuotes Software Corp."
|
||
|
#property link "https://www.mql5.com"
|
||
|
#property version "1.00"
|
||
|
#property strict
|
||
|
|
||
|
#include <Arrays\ArrayObj.mqh>
|
||
|
#include <Trade\PositionInfo.mqh>
|
||
|
#include "TradeExecutor.mqh"
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Paper Trade Position Class |
|
||
|
//+------------------------------------------------------------------+
|
||
|
class CPosition : public CObject
|
||
|
{
|
||
|
public:
|
||
|
ulong ticket; // Position ticket
|
||
|
string symbol; // Symbol
|
||
|
ENUM_POSITION_TYPE type; // Position type
|
||
|
double volume; // Position volume
|
||
|
double openPrice; // Open price
|
||
|
double stopLoss; // Stop loss level
|
||
|
double takeProfit; // Take profit level
|
||
|
datetime openTime; // Open time
|
||
|
|
||
|
// Constructor
|
||
|
CPosition(ulong t, string s, ENUM_POSITION_TYPE pt, double v, double op,
|
||
|
double sl, double tp, datetime ot) :
|
||
|
ticket(t), symbol(s), type(pt), volume(v), openPrice(op),
|
||
|
stopLoss(sl), takeProfit(tp), openTime(ot) {}
|
||
|
|
||
|
// Check if position is closed
|
||
|
bool IsClosed() const { return volume <= 0; }
|
||
|
|
||
|
// Calculate current profit
|
||
|
double CalculateProfit(double currentBid, double currentAsk) const
|
||
|
{
|
||
|
if(IsClosed()) return 0.0;
|
||
|
|
||
|
double profit = 0.0;
|
||
|
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||
|
|
||
|
if(type == POSITION_TYPE_BUY)
|
||
|
{
|
||
|
profit = (currentBid - openPrice) * volume * SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE) / point;
|
||
|
}
|
||
|
else if(type == POSITION_TYPE_SELL)
|
||
|
{
|
||
|
profit = (openPrice - currentAsk) * volume * SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE) / point;
|
||
|
}
|
||
|
|
||
|
return profit;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Paper Trading Class |
|
||
|
//+------------------------------------------------------------------+
|
||
|
class CPaperTrading
|
||
|
{
|
||
|
private:
|
||
|
CArrayObj m_positions; // Array of open positions
|
||
|
double m_balance; // Paper trading balance
|
||
|
double m_equity; // Current equity
|
||
|
double m_margin; // Used margin
|
||
|
double m_freeMargin; // Free margin
|
||
|
|
||
|
// Simulation parameters
|
||
|
bool m_simulateSlippage; // Simulate slippage
|
||
|
bool m_simulatePartialFills; // Simulate partial fills
|
||
|
double m_maxSlippagePips; // Maximum slippage in pips
|
||
|
double m_fillProbability; // Probability of fill (0-1)
|
||
|
|
||
|
// Statistics
|
||
|
int m_totalTrades; // Total number of trades
|
||
|
int m_winningTrades; // Number of winning trades
|
||
|
int m_losingTrades; // Number of losing trades
|
||
|
double m_totalProfit; // Total profit
|
||
|
double m_totalLoss; // Total loss
|
||
|
|
||
|
// Objects
|
||
|
CSymbolInfo *m_symbol; // Pointer to symbol info
|
||
|
|
||
|
// Private methods
|
||
|
double ApplySlippage(double price, ENUM_ORDER_TYPE type);
|
||
|
double ApplyPartialFill(double requestedVolume);
|
||
|
void UpdateMetrics();
|
||
|
|
||
|
public:
|
||
|
CPaperTrading();
|
||
|
~CPaperTrading();
|
||
|
|
||
|
// Initialization
|
||
|
bool Initialize(CSymbolInfo *symbol, double initialBalance = 10000.0,
|
||
|
bool simulateSlippage = true, bool simulatePartialFills = true,
|
||
|
double maxSlippagePips = 1.0, double fillProbability = 0.95);
|
||
|
|
||
|
// Trading operations
|
||
|
bool OpenPosition(ENUM_ORDER_TYPE type, double lots, double price,
|
||
|
double sl = 0, double tp = 0);
|
||
|
bool ClosePosition(ulong ticket, double lots = 0);
|
||
|
bool ModifyPosition(ulong ticket, double sl, double tp);
|
||
|
|
||
|
// Position management
|
||
|
bool SelectPosition(ulong ticket, CPosition *&position);
|
||
|
int TotalPositions() const { return m_positions.Total(); }
|
||
|
|
||
|
// Account information
|
||
|
double GetBalance() const { return m_balance; }
|
||
|
double GetEquity() const { return m_equity; }
|
||
|
double GetMargin() const { return m_margin; }
|
||
|
double GetFreeMargin() const { return m_freeMargin; }
|
||
|
|
||
|
// Statistics
|
||
|
int GetTotalTrades() const { return m_totalTrades; }
|
||
|
int GetWinningTrades() const { return m_winningTrades; }
|
||
|
int GetLosingTrades() const { return m_losingTrades; }
|
||
|
double GetTotalProfit() const { return m_totalProfit; }
|
||
|
double GetTotalLoss() const { return m_totalLoss; }
|
||
|
double GetWinRate() const
|
||
|
{
|
||
|
return (m_totalTrades > 0) ? (double)m_winningTrades / m_totalTrades * 100.0 : 0.0;
|
||
|
}
|
||
|
|
||
|
// Visualization
|
||
|
void UpdateVisualization();
|
||
|
|
||
|
// Update method called on each tick
|
||
|
void Update();
|
||
|
|
||
|
// Check if live trading is allowed based on paper trading results
|
||
|
bool IsLiveTradingAllowed();
|
||
|
};
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Constructor |
|
||
|
//+------------------------------------------------------------------+
|
||
|
CPaperTrading::CPaperTrading() : m_balance(10000.0),
|
||
|
m_equity(10000.0),
|
||
|
m_margin(0.0),
|
||
|
m_freeMargin(10000.0),
|
||
|
m_simulateSlippage(true),
|
||
|
m_simulatePartialFills(true),
|
||
|
m_maxSlippagePips(1.0),
|
||
|
m_fillProbability(0.95),
|
||
|
m_totalTrades(0),
|
||
|
m_winningTrades(0),
|
||
|
m_losingTrades(0),
|
||
|
m_totalProfit(0.0),
|
||
|
m_totalLoss(0.0),
|
||
|
m_symbol(NULL)
|
||
|
{
|
||
|
m_positions.Clear();
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Destructor |
|
||
|
//+------------------------------------------------------------------+
|
||
|
CPaperTrading::~CPaperTrading()
|
||
|
{
|
||
|
m_positions.Clear();
|
||
|
m_symbol = NULL;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Initialize paper trading |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CPaperTrading::Initialize(CSymbolInfo *symbol, double initialBalance,
|
||
|
bool simulateSlippage, bool simulatePartialFills,
|
||
|
double maxSlippagePips, double fillProbability)
|
||
|
{
|
||
|
if(symbol == NULL || initialBalance <= 0)
|
||
|
{
|
||
|
Print("Error: Invalid parameters in PaperTrading initialization");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_symbol = symbol;
|
||
|
m_balance = initialBalance;
|
||
|
m_equity = initialBalance;
|
||
|
m_freeMargin = initialBalance;
|
||
|
|
||
|
// Set simulation parameters
|
||
|
m_simulateSlippage = simulateSlippage;
|
||
|
m_simulatePartialFills = simulatePartialFills;
|
||
|
m_maxSlippagePips = maxSlippagePips;
|
||
|
m_fillProbability = MathMax(0.0, MathMin(1.0, fillProbability));
|
||
|
|
||
|
// Initialize statistics
|
||
|
m_totalTrades = 0;
|
||
|
m_winningTrades = 0;
|
||
|
m_losingTrades = 0;
|
||
|
m_totalProfit = 0.0;
|
||
|
m_totalLoss = 0.0;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Open a new position |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CPaperTrading::OpenPosition(ENUM_ORDER_TYPE type, double lots, double price,
|
||
|
double sl, double tp)
|
||
|
{
|
||
|
if(m_symbol == NULL || lots <= 0 || price <= 0)
|
||
|
{
|
||
|
Print("Error: Invalid parameters in OpenPosition");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Apply slippage if enabled
|
||
|
double executionPrice = ApplySlippage(price, type);
|
||
|
|
||
|
// Apply partial fill if enabled
|
||
|
double filledLots = m_simulatePartialFills ?
|
||
|
ApplyPartialFill(lots) : lots;
|
||
|
|
||
|
if(filledLots <= 0)
|
||
|
{
|
||
|
Print("Order not filled (partial fill simulation)");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Generate a unique ticket
|
||
|
static ulong lastTicket = 1000;
|
||
|
ulong ticket = ++lastTicket;
|
||
|
|
||
|
// Create and add the position
|
||
|
CPosition *position = new CPosition(ticket, m_symbol.Name(),
|
||
|
(type == ORDER_TYPE_BUY) ? POSITION_TYPE_BUY : POSITION_TYPE_SELL,
|
||
|
filledLots, executionPrice, sl, tp, TimeCurrent());
|
||
|
|
||
|
if(position == NULL)
|
||
|
{
|
||
|
Print("Error: Failed to create position object");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_positions.Add(position);
|
||
|
|
||
|
// Update metrics
|
||
|
UpdateMetrics();
|
||
|
|
||
|
// Update statistics
|
||
|
m_totalTrades++;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Close a position |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CPaperTrading::ClosePosition(ulong ticket, double lots = 0)
|
||
|
{
|
||
|
CPosition *position = NULL;
|
||
|
|
||
|
// Find the position
|
||
|
for(int i = 0; i < m_positions.Total(); i++)
|
||
|
{
|
||
|
CPosition *pos = m_positions.At(i);
|
||
|
if(pos != NULL && pos.ticket == ticket && !pos.IsClosed())
|
||
|
{
|
||
|
position = pos;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(position == NULL)
|
||
|
{
|
||
|
Print("Error: Position not found or already closed");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Calculate close price with slippage
|
||
|
double closePrice = ApplySlippage(
|
||
|
(position.type == POSITION_TYPE_BUY) ? m_symbol.Bid() : m_symbol.Ask(),
|
||
|
(position.type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY
|
||
|
);
|
||
|
|
||
|
// Calculate profit/loss
|
||
|
double profit = 0.0;
|
||
|
double point = m_symbol.Point();
|
||
|
double tickValue = m_symbol.TickValue();
|
||
|
|
||
|
if(position.type == POSITION_TYPE_BUY)
|
||
|
{
|
||
|
profit = (closePrice - position.openPrice) * position.volume * tickValue / point;
|
||
|
}
|
||
|
else // POSITION_TYPE_SELL
|
||
|
{
|
||
|
profit = (position.openPrice - closePrice) * position.volume * tickValue / point;
|
||
|
}
|
||
|
|
||
|
// Update balance and statistics
|
||
|
m_balance += profit;
|
||
|
|
||
|
if(profit >= 0)
|
||
|
{
|
||
|
m_winningTrades++;
|
||
|
m_totalProfit += profit;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_losingTrades++;
|
||
|
m_totalLoss -= profit; // Loss is stored as positive value
|
||
|
}
|
||
|
|
||
|
// Close the position or reduce its volume
|
||
|
if(lots <= 0 || lots >= position.volume)
|
||
|
{
|
||
|
// Close the entire position
|
||
|
position.volume = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Partial close
|
||
|
position.volume -= lots;
|
||
|
}
|
||
|
|
||
|
// Update metrics
|
||
|
UpdateMetrics();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Modify position's stop loss and take profit |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CPaperTrading::ModifyPosition(ulong ticket, double sl, double tp)
|
||
|
{
|
||
|
CPosition *position = NULL;
|
||
|
|
||
|
// Find the position
|
||
|
for(int i = 0; i < m_positions.Total(); i++)
|
||
|
{
|
||
|
CPosition *pos = m_positions.At(i);
|
||
|
if(pos != NULL && pos.ticket == ticket && !pos.IsClosed())
|
||
|
{
|
||
|
position = pos;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(position == NULL)
|
||
|
{
|
||
|
Print("Error: Position not found or already closed");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Update stop loss and take profit
|
||
|
position.stopLoss = sl;
|
||
|
position.takeProfit = tp;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Select position by ticket |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CPaperTrading::SelectPosition(ulong ticket, CPosition *&position)
|
||
|
{
|
||
|
for(int i = 0; i < m_positions.Total(); i++)
|
||
|
{
|
||
|
CPosition *pos = m_positions.At(i);
|
||
|
if(pos != NULL && pos.ticket == ticket && !pos.IsClosed())
|
||
|
{
|
||
|
position = pos;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
position = NULL;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Apply slippage to price |
|
||
|
//+------------------------------------------------------------------+
|
||
|
double CPaperTrading::ApplySlippage(double price, ENUM_ORDER_TYPE type)
|
||
|
{
|
||
|
if(!m_simulateSlippage || m_maxSlippagePips <= 0)
|
||
|
return price;
|
||
|
|
||
|
// Generate random slippage (can be positive or negative)
|
||
|
double slippagePips = ((double)MathRand() / 32767.0 - 0.5) * 2.0 * m_maxSlippagePips;
|
||
|
double point = m_symbol.Point();
|
||
|
|
||
|
// For buy orders, slippage is added to the price (worse execution)
|
||
|
// For sell orders, slippage is subtracted from the price (worse execution)
|
||
|
if(type == ORDER_TYPE_BUY)
|
||
|
return price + slippagePips * point;
|
||
|
else
|
||
|
return price - slippagePips * point;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Apply partial fill to order |
|
||
|
//+------------------------------------------------------------------
|
||
|
double CPaperTrading::ApplyPartialFill(double requestedVolume)
|
||
|
{
|
||
|
if(!m_simulatePartialFills || m_fillProbability >= 1.0)
|
||
|
return requestedVolume;
|
||
|
|
||
|
// Check if order is filled based on probability
|
||
|
if(((double)MathRand() / 32767.0) > m_fillProbability)
|
||
|
return 0.0; // Order not filled
|
||
|
|
||
|
// For simplicity, we either fill the entire order or nothing
|
||
|
// In a more advanced simulation, you could implement partial fills
|
||
|
return requestedVolume;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Update account metrics |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void CPaperTrading::UpdateMetrics()
|
||
|
{
|
||
|
if(m_symbol == NULL)
|
||
|
return;
|
||
|
|
||
|
// Update current prices
|
||
|
m_symbol.RefreshRates();
|
||
|
|
||
|
// Calculate equity and margin
|
||
|
double floatingProfit = 0.0;
|
||
|
m_margin = 0.0;
|
||
|
|
||
|
// Calculate floating P/L and used margin for all open positions
|
||
|
for(int i = 0; i < m_positions.Total(); i++)
|
||
|
{
|
||
|
CPosition *pos = m_positions.At(i);
|
||
|
if(pos != NULL && !pos.IsClosed())
|
||
|
{
|
||
|
// Calculate floating profit/loss
|
||
|
if(pos.type == POSITION_TYPE_BUY)
|
||
|
{
|
||
|
floatingProfit += (m_symbol.Bid() - pos.openPrice) * pos.volume *
|
||
|
m_symbol.TickValue() / m_symbol.Point();
|
||
|
}
|
||
|
else // POSITION_TYPE_SELL
|
||
|
{
|
||
|
floatingProfit += (pos.openPrice - m_symbol.Ask()) * pos.volume *
|
||
|
m_symbol.TickValue() / m_symbol.Point();
|
||
|
}
|
||
|
|
||
|
// Calculate margin (simplified)
|
||
|
m_margin += pos.volume * pos.openPrice / 100.0; // 1:100 leverage
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update equity and free margin
|
||
|
m_equity = m_balance + floatingProfit;
|
||
|
m_freeMargin = m_equity - m_margin;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Update method called on each tick |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void CPaperTrading::Update()
|
||
|
{
|
||
|
// Update metrics and visualization
|
||
|
UpdateMetrics();
|
||
|
UpdateVisualization();
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Check if live trading is allowed based on paper trading results |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CPaperTrading::IsLiveTradingAllowed()
|
||
|
{
|
||
|
// Check if we have enough trades
|
||
|
if(m_totalTrades < 5) // Minimum number of trades required
|
||
|
return false;
|
||
|
|
||
|
// Check win rate (e.g., at least 60% win rate)
|
||
|
double winRate = GetWinRate();
|
||
|
if(winRate < 60.0)
|
||
|
return false;
|
||
|
|
||
|
// Check profit factor (e.g., at least 1.5)
|
||
|
double profitFactor = (m_totalLoss != 0) ? m_totalProfit / m_totalLoss : m_totalProfit;
|
||
|
if(profitFactor < 1.5)
|
||
|
return false;
|
||
|
|
||
|
// All conditions met, allow live trading
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Update visualization |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void CPaperTrading::UpdateVisualization()
|
||
|
{
|
||
|
// Update metrics first
|
||
|
UpdateMetrics();
|
||
|
|
||
|
// Create or update panel
|
||
|
string panelName = "PaperTrading_Panel";
|
||
|
|
||
|
// Create panel background
|
||
|
if(ObjectFind(0, panelName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, panelName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 150);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_XSIZE, 200);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_YSIZE, 120);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_BGCOLOR, clrWhiteSmoke);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrDimGray);
|
||
|
ObjectSetInteger(0, panelName, OBJPROP_BACK, false);
|
||
|
}
|
||
|
|
||
|
// Create or update title
|
||
|
string titleName = panelName + "_Title";
|
||
|
if(ObjectFind(0, titleName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, titleName, OBJ_LABEL, 0, 0, 0);
|
||
|
ObjectSetInteger(0, titleName, OBJPROP_COLOR, clrBlack);
|
||
|
ObjectSetInteger(0, titleName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||
|
ObjectSetInteger(0, titleName, OBJPROP_XDISTANCE, 20);
|
||
|
ObjectSetInteger(0, titleName, OBJPROP_YDISTANCE, 160);
|
||
|
ObjectSetInteger(0, titleName, OBJPROP_FONTSIZE, 10);
|
||
|
}
|
||
|
ObjectSetString(0, titleName, OBJPROP_TEXT, "PAPER TRADING");
|
||
|
|
||
|
// Create or update balance label
|
||
|
string balanceName = panelName + "_Balance";
|
||
|
if(ObjectFind(0, balanceName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, balanceName, OBJ_LABEL, 0, 0, 0);
|
||
|
ObjectSetInteger(0, balanceName, OBJPROP_COLOR, clrBlack);
|
||
|
ObjectSetInteger(0, balanceName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||
|
ObjectSetInteger(0, balanceName, OBJPROP_XDISTANCE, 20);
|
||
|
ObjectSetInteger(0, balanceName, OBJPROP_YDISTANCE, 180);
|
||
|
ObjectSetInteger(0, balanceName, OBJPROP_FONTSIZE, 9);
|
||
|
}
|
||
|
ObjectSetString(0, balanceName, OBJPROP_TEXT, "Balance: " + DoubleToString(m_balance, 2));
|
||
|
|
||
|
// Create or update equity label
|
||
|
string equityName = panelName + "_Equity";
|
||
|
if(ObjectFind(0, equityName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, equityName, OBJ_LABEL, 0, 0, 0);
|
||
|
ObjectSetInteger(0, equityName, OBJPROP_COLOR, clrBlack);
|
||
|
ObjectSetInteger(0, equityName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||
|
ObjectSetInteger(0, equityName, OBJPROP_XDISTANCE, 20);
|
||
|
ObjectSetInteger(0, equityName, OBJPROP_YDISTANCE, 200);
|
||
|
ObjectSetInteger(0, equityName, OBJPROP_FONTSIZE, 9);
|
||
|
}
|
||
|
ObjectSetString(0, equityName, OBJPROP_TEXT, "Equity: " + DoubleToString(m_equity, 2));
|
||
|
|
||
|
// Create or update margin label
|
||
|
string marginName = panelName + "_Margin";
|
||
|
if(ObjectFind(0, marginName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, marginName, OBJ_LABEL, 0, 0, 0);
|
||
|
ObjectSetInteger(0, marginName, OBJPROP_COLOR, clrBlack);
|
||
|
ObjectSetInteger(0, marginName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||
|
ObjectSetInteger(0, marginName, OBJPROP_XDISTANCE, 20);
|
||
|
ObjectSetInteger(0, marginName, OBJPROP_YDISTANCE, 220);
|
||
|
ObjectSetInteger(0, marginName, OBJPROP_FONTSIZE, 9);
|
||
|
}
|
||
|
ObjectSetString(0, marginName, OBJPROP_TEXT, "Margin: " + DoubleToString(m_margin, 2));
|
||
|
|
||
|
// Create or update free margin label
|
||
|
string freeMarginName = panelName + "_FreeMargin";
|
||
|
if(ObjectFind(0, freeMarginName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, freeMarginName, OBJ_LABEL, 0, 0, 0);
|
||
|
ObjectSetInteger(0, freeMarginName, OBJPROP_COLOR, clrBlack);
|
||
|
ObjectSetInteger(0, freeMarginName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||
|
ObjectSetInteger(0, freeMarginName, OBJPROP_XDISTANCE, 20);
|
||
|
ObjectSetInteger(0, freeMarginName, OBJPROP_YDISTANCE, 240);
|
||
|
ObjectSetInteger(0, freeMarginName, OBJPROP_FONTSIZE, 9);
|
||
|
}
|
||
|
ObjectSetString(0, freeMarginName, OBJPROP_TEXT, "Free Margin: " + DoubleToString(m_freeMargin, 2));
|
||
|
|
||
|
// Update the chart
|
||
|
ChartRedraw();
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|