456 lines
21 KiB
MQL5
456 lines
21 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| StopLossManagement.mqh |
|
|
//| Jay Davis |
|
|
//| 512jay.github.io |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Jay Davis"
|
|
#property link "512jay.github.io"
|
|
// v1
|
|
bool Debug_StopLossManagment = false;
|
|
input group ""
|
|
input group "|----------Stop Loss Management----------|"
|
|
input bool useVirtualStops = true; // Use virtual stops
|
|
input int MinimumPointsForStopLossMove = 1; // Minimum number of points to move any Stoploss
|
|
input int SendNoticeTimer = 60; // Minutes between stoploss notices
|
|
input group "||--- Average True Range Settings"
|
|
input bool useATRStopLoss = true; // Use ATR for stop loss?
|
|
input double atrMultiplier = 1; // ATR Multiplier
|
|
input ENUM_TIMEFRAMES TimeframeOfATR = PERIOD_CURRENT; // Timeframe for ATR calculations
|
|
input int PeriodsInATR = 14; // periods in calculation
|
|
input group "||--- Percentage Stop Loss Settings"
|
|
input bool usePercentageStopLoss = false; // Use a percentage stop loss?
|
|
input double BuyStopLossPercentage = 0.1; // BUY Stop-Loss Percentage, non zero, non negative;
|
|
input double SellStopLossPercentage = 0.1; // SELL Stop-Loss Percentage, non zero, non negative;
|
|
input group "||--- Idle Stoploss movement"
|
|
input bool BuyMoveStopLossIfIdle = false; // Move BUY Stop-Loss if Idle?
|
|
input int BuyIdleMinutesRequiringAMove = 25; // Number of Idle minutes requiring a BUY Stop-Loss move
|
|
input int BuyIdleMovements = 5; // Number of possible BUY idle movments
|
|
input double BuyPercentageToMoveStopLossIfIdle = 0.01; // % to move BUY Stop-Loss when idle
|
|
input bool SellMoveStopLossIfIdle = false; // Move SELL Stop-Loss if Idle?
|
|
input int SellIdleMinutesRequiringAMove = 15; // Number of Idle minutes requiring a SELL Stop-Loss move
|
|
input double SellPercentageToMoveStopLossIfIdle = 0.01; // % to move SELL Stop-Loss when idle
|
|
input int SellIdleMovements = 5; // Number of possible SELL idle movments
|
|
datetime MostRecentNoticeTime = 0;
|
|
// Global variables
|
|
double trailingDistance = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Sends the current stops to WhatsApp by SendNoticeTimer |
|
|
//+------------------------------------------------------------------+
|
|
void SendModifyNotice()
|
|
{
|
|
if(TimeCurrent() - (SendNoticeTimer * 60) > MostRecentNoticeTime)
|
|
{
|
|
int total = ArraySize(Virt);
|
|
for(int i = 0; i < total; i++)
|
|
if(Virt[i].active == true)
|
|
NoticeMessage("ITSL", Virt[i], 3);
|
|
MostRecentNoticeTime = TimeCurrent();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gets and returns the ATR value |
|
|
//| Returns ATR value |
|
|
//+------------------------------------------------------------------+
|
|
double AverageTrueRange(string name, ENUM_TIMEFRAMES period, int atr_periods)
|
|
{
|
|
int handle = iATR(name, period, atr_periods);
|
|
double atr[];
|
|
//--- if the handle is not created
|
|
if(handle == INVALID_HANDLE)
|
|
{
|
|
//--- tell about the failure and output the error code
|
|
PrintFormat("Failed to create handle of the iATR indicator for the symbol %s/%s, error code %d",
|
|
name,
|
|
EnumToString(period),
|
|
GetLastError());
|
|
//--- the indicator is stopped early
|
|
return(INIT_FAILED);
|
|
}
|
|
CopyBuffer(handle, 0, 0, 1, atr);
|
|
trailingDistance = NormalizeDouble(atr[0] * atrMultiplier,
|
|
(int)SymbolInfoInteger(name, SYMBOL_DIGITS));
|
|
return trailingDistance;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the amount for the given percentage. |
|
|
//+------------------------------------------------------------------+
|
|
double GetPercentage(string name, ENUM_ORDER_TYPE dir, double percentage)
|
|
{
|
|
double bid, ask, amount = 0.0;
|
|
if(dir == ORDER_TYPE_BUY)
|
|
{
|
|
bid = SymbolInfoDouble(name, SYMBOL_BID);
|
|
amount = bid * percentage / 100;
|
|
}
|
|
if(dir == ORDER_TYPE_SELL)
|
|
{
|
|
ask = SymbolInfoDouble(name, SYMBOL_ASK);
|
|
amount = ask * percentage / 100;
|
|
}
|
|
trailingDistance = NormalizeDouble(amount,
|
|
(int)SymbolInfoInteger(name, SYMBOL_DIGITS));
|
|
return trailingDistance;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the stoploss based on input values |
|
|
//+------------------------------------------------------------------+
|
|
double GetStopLoss(string name, ENUM_ORDER_TYPE dir, bool checkIdle, bool print = false)
|
|
{
|
|
double bid = 0, ask = 0, percentage_amount = 0, atr_amount = 0, stopLoss = 0;
|
|
if(dir == ORDER_TYPE_BUY)
|
|
{
|
|
if(useATRStopLoss && !checkIdle)
|
|
{
|
|
atr_amount = AverageTrueRange(name, TimeframeOfATR, PeriodsInATR);
|
|
stopLoss = atr_amount;
|
|
//if(percentage_amount != 0)
|
|
// stopLoss = fmin(percentage_amount, atr_amount);
|
|
}
|
|
if(usePercentageStopLoss && !checkIdle)
|
|
{
|
|
percentage_amount = GetPercentage(name, dir, BuyStopLossPercentage);
|
|
stopLoss = percentage_amount;
|
|
}
|
|
if(checkIdle && BuyMoveStopLossIfIdle)
|
|
{
|
|
percentage_amount = GetPercentage(name, dir, BuyPercentageToMoveStopLossIfIdle);
|
|
stopLoss = percentage_amount;
|
|
}
|
|
bid = SymbolInfoDouble(Sym, SYMBOL_BID);
|
|
if(print)
|
|
Print("Bid ", bid, ", stoploss ", stopLoss, " bid - sl ", bid - stopLoss);
|
|
stopLoss = bid - stopLoss;//trailingDistance;
|
|
}
|
|
else
|
|
if(dir == ORDER_TYPE_SELL)
|
|
{
|
|
if(useATRStopLoss && !checkIdle)
|
|
{
|
|
atr_amount = AverageTrueRange(name, TimeframeOfATR, PeriodsInATR);
|
|
stopLoss = atr_amount;
|
|
//if(percentage_amount != 0)
|
|
// stopLoss = fmin(percentage_amount, atr_amount);
|
|
}
|
|
if(usePercentageStopLoss && !checkIdle)
|
|
{
|
|
percentage_amount = GetPercentage(name, dir, SellStopLossPercentage);
|
|
stopLoss = percentage_amount;
|
|
}
|
|
if(SellMoveStopLossIfIdle && checkIdle)
|
|
{
|
|
percentage_amount = GetPercentage(name, dir, SellStopLossPercentage);
|
|
stopLoss = percentage_amount;
|
|
}
|
|
ask = SymbolInfoDouble(Sym, SYMBOL_ASK);
|
|
if(print)
|
|
Print("Ask ", ask, ", stoploss ", stopLoss, " ask + sl ", ask + stopLoss);
|
|
stopLoss = ask + stopLoss;//trailingDistance;
|
|
}
|
|
if(stopLoss == ask || stopLoss == bid)
|
|
return (0);
|
|
else
|
|
return NormalizeDouble(stopLoss, (int)SymbolInfoInteger(name, SYMBOL_DIGITS));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void ModifyStopLoss()
|
|
{
|
|
SendModifyNotice();
|
|
if(BuyMoveStopLossIfIdle || SellMoveStopLossIfIdle)
|
|
ModifyStopLossByIdleTime();
|
|
if(usePercentageStopLoss || useATRStopLoss)
|
|
ModifyStops();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the current stoploss and creates one if it does not exist|
|
|
//+------------------------------------------------------------------+
|
|
double CurrentServerStopLoss()
|
|
{
|
|
double currentStopLoss = PositionGetDouble(POSITION_SL);
|
|
if(currentStopLoss == 0 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
currentStopLoss = GetStopLoss(Sym, ORDER_TYPE_BUY, false);
|
|
if(!trade.PositionModify(Sym, currentStopLoss, 0))
|
|
Print(" Buy Modify fails, bid: ", SymbolInfoDouble(Sym, SYMBOL_BID), " current stop loss: ", currentStopLoss);
|
|
}
|
|
if(currentStopLoss == 0 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
|
{
|
|
currentStopLoss = GetStopLoss(Sym, ORDER_TYPE_SELL, false);;
|
|
if(!trade.PositionModify(Sym, currentStopLoss, 0))
|
|
Print(" Sell Modify fails, ask: ", SymbolInfoDouble(Sym, SYMBOL_ASK), " current stop loss: ", currentStopLoss);
|
|
}
|
|
return currentStopLoss;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the current stoploss and creates one if it does not exist|
|
|
//+------------------------------------------------------------------+
|
|
double CurrentVirtualStopLoss(int i)
|
|
{
|
|
if(Virt[i].request.sl == 0)
|
|
{
|
|
if(Virt[i].request.type == ORDER_TYPE_BUY)
|
|
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_BUY, false);
|
|
if(Virt[i].request.type == ORDER_TYPE_SELL)
|
|
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_SELL, false);
|
|
}
|
|
return Virt[i].request.sl;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the current buy stop loss value with trailing |
|
|
//+------------------------------------------------------------------+
|
|
double TrailingBuyStopLoss()
|
|
{
|
|
double bid = SymbolInfoDouble(Sym, SYMBOL_BID);
|
|
return bid - trailingDistance;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the current sell stop loss value with trailing |
|
|
//+------------------------------------------------------------------+
|
|
double TrailingSellStopLoss()
|
|
{
|
|
double ask = SymbolInfoDouble(Sym, SYMBOL_ASK);
|
|
return ask + trailingDistance;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void ModifyServerSideStops()
|
|
{
|
|
double buyStopLoss = 0; // = GetStopLoss(Sym, Buy, false);
|
|
double sellStopLoss = 0; // = GetStopLoss(Sym, Sell, false);
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
if(Sym == PositionGetSymbol(i))
|
|
{
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
if(TrailingBuyStopLoss() - CurrentServerStopLoss() > MinMovement)
|
|
if(!trade.PositionModify(Sym, TrailingBuyStopLoss(), 0))
|
|
Print("[FAIL]*** Failed BUY Stop Loss at ", TrailingBuyStopLoss(),
|
|
" Current was ", CurrentServerStopLoss());
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
|
if(CurrentServerStopLoss() - TrailingSellStopLoss() > MinMovement)
|
|
if(!trade.PositionModify(Sym, TrailingSellStopLoss(), 0))
|
|
Print("[FAIL]*** Failed SELL Stop Loss at ", TrailingSellStopLoss(),
|
|
" Current was ", CurrentServerStopLoss());
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modifies virtual stop losses |
|
|
//+------------------------------------------------------------------+
|
|
void ModifyVirtualStops()
|
|
{
|
|
//Print(__FUNCTION__);
|
|
double newStopLoss;
|
|
for(int i = ArraySize(Virt) - 1; i >= 0; i--)
|
|
if(Virt[i].active)
|
|
{
|
|
double oldStopLoss = Virt[i].request.sl;
|
|
if(oldStopLoss == 0.0)
|
|
oldStopLoss = GetStopLoss(Virt[i].request.symbol, Virt[i].request.type, false);
|
|
newStopLoss = GetStopLoss(Virt[i].request.symbol, Virt[i].request.type, false);
|
|
if(Virt[i].request.type == ORDER_TYPE_BUY)
|
|
if(newStopLoss - oldStopLoss > MinMovement)
|
|
{
|
|
Print("Buy trailing stoploss ", TrailingBuyStopLoss(), " - current virtual stoploss ", CurrentVirtualStopLoss(i), " is greater than min movement ", MinMovement);
|
|
Virt[i].request.sl = newStopLoss;
|
|
InsertResumeItem(Virt[i], DatabaseName);
|
|
DisplayLines(Virt[i]);
|
|
if(Virt[i].request.sl > Virt[i].request.price)
|
|
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrNavy);
|
|
if(!MQLInfoInteger(MQL_TESTER))
|
|
{
|
|
double dist = Virt[i].request.sl - oldStopLoss;
|
|
dist /= SymbolInfoDouble(Virt[i].request.symbol, SYMBOL_POINT);
|
|
int distance = (int) dist;
|
|
string s = "s";
|
|
if(distance == 1)
|
|
s = "";
|
|
string stoplossMessage = MQLInfoString(MQL_PROGRAM_NAME) + " " + Virt[i].request.symbol +
|
|
" stoploss: " + (string)oldStopLoss + " moved " +
|
|
(string)distance + " point" + s + " to: "
|
|
+ (string)Virt[i].request.sl;
|
|
if(Debug_StopLossManagment)
|
|
{
|
|
SendToWhatsAppAPI("[Debug]::" + stoplossMessage);
|
|
Print(stoplossMessage);
|
|
} //SendModifyNotice(Virt[i].request.type, Virt[i].request.sl, Virt[i].request.volume, i);
|
|
//Print(Virt[i].request.symbol, " stoploss: ", oldStopLoss, " moved ", distance, " points", s, " to: ", Virt[i].request.sl);
|
|
}
|
|
HLineMove(0, "StopLoss", Virt[i].request.sl);
|
|
}
|
|
if(Virt[i].request.type == ORDER_TYPE_SELL)
|
|
if(CurrentVirtualStopLoss(i) - newStopLoss > MinMovement)
|
|
{
|
|
Print("Sell current virtual stoploss ", CurrentVirtualStopLoss(i), " - trailing stoploss ", TrailingSellStopLoss(), " is greater than min movement ", MinMovement);
|
|
Virt[i].request.sl = newStopLoss;
|
|
InsertResumeItem(Virt[i], DatabaseName);
|
|
DisplayLines(Virt[i]);
|
|
if(Virt[i].request.sl < Virt[i].request.price)
|
|
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrNavy);
|
|
if(!MQLInfoInteger(MQL_TESTER))
|
|
{
|
|
double dist = Virt[i].request.sl - oldStopLoss;
|
|
dist /= SymbolInfoDouble(Virt[i].request.symbol, SYMBOL_POINT);
|
|
int distance = (int) dist;
|
|
string s = "s";
|
|
if(distance == 1 || distance == -1)
|
|
s = "";
|
|
string stoplossMessage = MQLInfoString(MQL_PROGRAM_NAME) + " " + Virt[i].request.symbol +
|
|
" stoploss: " + (string)oldStopLoss + " moved " +
|
|
(string)distance + " point" + s + " to: "
|
|
+ (string)Virt[i].request.sl;
|
|
if(Debug_StopLossManagment)
|
|
{
|
|
SendToWhatsAppAPI("[Debug]::" + stoplossMessage);
|
|
Print(stoplossMessage);
|
|
}
|
|
//SendModifyNotice(Virt[i].request.type, Virt[i].request.sl, Virt[i].request.volume, i);
|
|
//Print(Virt[i].request.symbol, " stoploss: ", oldStopLoss, " moved ", distance, " point", s, " to: ", Virt[i].request.sl);
|
|
}
|
|
HLineMove(0, "StopLoss", Virt[i].request.sl);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void ModifyStops()
|
|
{
|
|
if(!useVirtualStops)
|
|
ModifyServerSideStops();
|
|
ModifyVirtualStops();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks and modifies stop losses that are too long idle |
|
|
//+------------------------------------------------------------------+
|
|
void ModifyStopLossByIdleTime()
|
|
{
|
|
datetime idleTime = TimeCurrent() - TimeStopLossWasLastModified;
|
|
double idleTimeInMinutes = (long) idleTime / 60.0; // 1 min = 60 seconds;
|
|
bool aModificationHappened = false;
|
|
string type = " BUY ";
|
|
if((idleTimeInMinutes > BuyIdleMinutesRequiringAMove
|
|
&& IdleMovements < BuyIdleMovements
|
|
&& BuyMoveStopLossIfIdle)
|
|
|| (idleTimeInMinutes > SellIdleMinutesRequiringAMove
|
|
&& IdleMovements < SellIdleMovements
|
|
&& SellMoveStopLossIfIdle))
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
if(Sym == PositionGetSymbol(i))
|
|
{
|
|
// Get the current stop loss
|
|
double currentStopLoss = PositionGetDouble(POSITION_SL);
|
|
// If current stop loss = 0 skip
|
|
if(currentStopLoss == 0)
|
|
continue;
|
|
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
|
//double currentStopLoss = PositionGetDouble(POSITION_SL);
|
|
// Get the direction
|
|
if(BuyMoveStopLossIfIdle)
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
if(GetStopLoss(Sym, ORDER_TYPE_BUY, true) - currentStopLoss > MinMovement)
|
|
if(trade.PositionModify(ticket, GetStopLoss(Sym, ORDER_TYPE_BUY, true), 0))
|
|
aModificationHappened = true;
|
|
else
|
|
Print("[FAIL]*** Failed BUY Stop Loss at ", GetStopLoss(Sym, ORDER_TYPE_BUY, true),
|
|
" Current was ", currentStopLoss);
|
|
if(SellMoveStopLossIfIdle)
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
|
if(currentStopLoss - GetStopLoss(Sym, ORDER_TYPE_SELL, true) > MinMovement)
|
|
if(trade.PositionModify(ticket, GetStopLoss(Sym, ORDER_TYPE_SELL, true), 0))
|
|
{
|
|
type = " SELL ";
|
|
aModificationHappened = true;
|
|
}
|
|
else
|
|
Print("[FAIL]*** Failed SELL Stop Loss at ", GetStopLoss(Sym, ORDER_TYPE_SELL, true),
|
|
" Current was ", currentStopLoss);
|
|
}
|
|
for(int i = ArraySize(Virt) - 1; i >= 0; i--)
|
|
if(Virt[i].active)
|
|
{
|
|
// Get the current stop loss
|
|
double currentStopLoss = Virt[i].request.sl;
|
|
// Get the current stop loss
|
|
ulong ticket = Virt[i].request.order;
|
|
// Get the direction
|
|
if(BuyMoveStopLossIfIdle)
|
|
if(Virt[i].request.type == ORDER_TYPE_BUY)
|
|
if(GetStopLoss(Sym, ORDER_TYPE_BUY, true) - currentStopLoss > MinMovement)
|
|
{
|
|
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_BUY, true);
|
|
aModificationHappened = true;
|
|
}
|
|
if(SellMoveStopLossIfIdle)
|
|
if(Virt[i].request.type == ORDER_TYPE_SELL)
|
|
if(currentStopLoss - GetStopLoss(Sym, ORDER_TYPE_SELL, true) > MinMovement)
|
|
{
|
|
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_SELL, true);
|
|
aModificationHappened = true;
|
|
}
|
|
}
|
|
}
|
|
if(aModificationHappened)
|
|
{
|
|
if(!MQLInfoInteger(MQL_TESTER))
|
|
Print("[MOVED", type, "STOPLOSS BY IDLE TIME] movement #", IdleMovements);
|
|
IdleMovements++;
|
|
TimeStopLossWasLastModified = TimeCurrent();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
////+------------------------------------------------------------------+
|
|
////| Modifies the stoploss if needed |
|
|
////+------------------------------------------------------------------+
|
|
//void ModifyStopLossByPercentage()
|
|
// {
|
|
// double buyStopLoss = GetStopLoss(Sym, Buy, false);
|
|
// double sellStopLoss = GetStopLoss(Sym, Sell, false);
|
|
//
|
|
// for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
// if(Sym == PositionGetSymbol(i))
|
|
// {
|
|
// // Get the current stop loss
|
|
// double currentStopLoss = PositionGetDouble(POSITION_SL);
|
|
// // If current stop loss = 0 skip
|
|
// if(currentStopLoss == 0)
|
|
// continue;
|
|
//
|
|
// ulong ticket = PositionGetInteger(POSITION_TICKET);
|
|
// //double currentStopLoss = PositionGetDouble(POSITION_SL);
|
|
// // Get the direction
|
|
// if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY
|
|
// && currentStopLoss < buyStopLoss)
|
|
// if(buyStopLoss - currentStopLoss > MinMovement)
|
|
// if(!trade.PositionModify(ticket, buyStopLoss, 0))
|
|
// Print("[FAIL]*** Failed BUY Stop Loss at ", buyStopLoss,
|
|
// " Current was ", currentStopLoss);
|
|
// if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL
|
|
// && currentStopLoss > sellStopLoss)
|
|
// if(currentStopLoss - sellStopLoss > MinMovement)
|
|
// if(!trade.PositionModify(ticket, sellStopLoss, 0))
|
|
// Print("[FAIL]*** Failed SELL Stop Loss at ", sellStopLoss,
|
|
// " Current was ", currentStopLoss);
|
|
// }
|
|
//
|
|
// for(int i = ArraySize(Virt) - 1; i >= 0; i--)
|
|
// if(Virt[i].active)
|
|
// {
|
|
// double currentStopLoss = Virt[i].request.sl;
|
|
// // Get the current stop loss
|
|
// ulong ticket = Virt[i].request.order;
|
|
// //double currentStopLoss = PositionGetDouble(POSITION_SL);
|
|
// // Get the direction
|
|
// if(Virt[i].request.type == ORDER_TYPE_BUY
|
|
// && currentStopLoss < buyStopLoss)
|
|
// if(buyStopLoss - currentStopLoss > MinMovement)
|
|
// Virt[i].request.sl = buyStopLoss;
|
|
// if(Virt[i].request.type == ORDER_TYPE_SELL
|
|
// && currentStopLoss > sellStopLoss)
|
|
// if(currentStopLoss - sellStopLoss > MinMovement)
|
|
// Virt[i].request.sl = sellStopLoss;
|
|
// }
|
|
// }
|