ea_price_action.mq5
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «ea_price_action.mq5» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Данный продукт представляет собой торговый советник для платформы MetaTrader 5, написанный на языке MQL5. Советник автоматически анализирует рыночную ситуацию и открывает сделки в направлении текущего движения цены, используя фильтрацию по магическому номеру для управления позициями.
Советник поддерживает управление рисками через стоп-лоссы и тейк-профиты, задаваемые в пунктах, а также позволяет настраивать размер лота и его множитель для последующих сделок. Доступны ограничения по времени торговли и максимальному количеству одновременных уровней позиций.
Внутри терминала отображается информационная панель с данными о балансе, эквити, спреде и текущей прибыли. Торговые операции выполняются через стандартные API MetaTrader 5 с проверкой допустимых режимов торговли на символе.
Настройки программы
| Настройка | Описание |
|---|---|
| EA_Name | Имя советника — Имя советника, отображаемое на информационной панели |
| Use_TradeAgain | Повторная торговля — Описание режима повторной торговли |
| TradeAgain | Повторная торговля — Переключатель повторной торговли после завершения цикла |
| Use_Loop | Ограничение циклов — Включение ограничения количества торговых циклов |
| Loop | Количество циклов — Максимальное количество торговых циклов за сессию |
| StartTrade | Час начала торговли — Час начала разрешённой торговли в серверном времени |
| EndTrade | Час окончания торговли — Час окончания разрешённой торговли в серверном времени |
| Use_DbLots | Режим расчёта лота — Включение режима расчёта лота с множителем |
| DbLots | Тип лота — Тип расчёта лота: фиксированный или с использованием множителя |
| Lots | Размер лота — Базовый размер лота для открытия позиции |
| SL | Стоп-лосс — Стоп-лосс в пунктах для каждой позиции |
| TP | Тейк-профит — Тейк-профит в пунктах для каждой позиции |
| Distance | Расстояние открытия — Расстояние в пунктах для открытия новой позиции относительно последней |
| Multiplier | Множитель лота — Множитель размера лота при последующих сделках |
| MaxLevel | Максимум позиций — Максимальное количество одновременных открытых позиций |
| LotsDecimal | Знаки лота — Количество знаков после запятой при расчёте размера лота |
| MagicNumber | Магический номер — Магический номер для идентификации позиций советника |
| EA_Comment | Комментарий сделки — Комментарий, добавляемый к торговым сделкам |
| MoneyPerLot | Сумма на лот — Сумма на лот для расчёта текущей прибыли на панели |
Поиск по коду файла
#property version "1.0"
input string EA_Name = "EA HOKKYDJONG";
input string Use_TradeAgain = "If => true,EA will trade again,If => false => EA will Off";
input bool TradeAgain = true;
input string Use_Loop = "Example = 10,EA will trader for 10 Laps";
input int Loop = 10000;
int trade_number;
input int StartTrade = 0;
input int EndTrade = 24;
input string Use_DbLots = "If = 1-> Use Multiplier Lot, If = 2-> Use Fixed Lot";
input int DbLots = 1;
input double Lots = 0.01;
input double SL = 0.0;
input double TP = 4.0;
input double Distance = 3.0;
input double Multiplier = 1.6;
input int MaxLevel = 20;
double Gd_176 = 3.0;
input double LotsDecimal = 2.0;
input int MagicNumber = 163991;
input string EA_Comment = "ea_hokkydjong";
double net_take_profit;
double average_open_price;
double last_buy_price;
double last_sell_price;
datetime G_time_264 = 0;
int last_trades_count = 0;
double lot_size;
int i = 0;
int trades_count;
bool open_new = false;
bool open_buy = false;
bool open_sell = false;
ulong order_ticket;
bool move_net_take_profit = false;
int Gi_324 = 65535;
int Gi_328 = 65535;
int Gi_332 = 16776960;
double Gd_336;
input double MoneyPerLot = 1.7;
string IndicatorObjPrefix;
bool NamesCollision(const string name)
{
for(int k = ObjectsTotal(0); k >= 0; k--)
{
if(StringFind(ObjectName(0, k), name) == 0)
{
return true;
}
}
return false;
}
string GenerateIndicatorPrefix(const string target)
{
for(int i = 0; i < 1000; ++i)
{
string prefix = target + "_" + IntegerToString(i);
if(!NamesCollision(prefix))
{
return prefix;
}
}
return target;
}
#ifndef OrderSide_IMP
#define OrderSide_IMP
enum OrderSide
{
BuySide,
SellSide
};
OrderSide GetOppositeSide(OrderSide side)
{
return side == BuySide ? SellSide : BuySide;
}
#endif
#ifndef ICondition_IMP
#define ICondition_IMP
interface ICondition
{
public:
virtual void AddRef() = 0;
virtual void Release() = 0;
virtual bool IsPass(const int period, const datetime date) = 0;
virtual string GetLogMessage(const int period, const datetime date) = 0;
};
#endif
#ifndef IAction_IMP
interface IAction
{
public:
virtual void AddRef() = 0;
virtual void Release() = 0;
virtual bool DoAction(const int period, const datetime date) = 0;
};
#define IAction_IMP
#endif
#ifndef ActionOnConditionController_IMP
#define ActionOnConditionController_IMP
class ActionOnConditionController
{
bool _finished;
ICondition *_condition;
IAction* _action;
public:
ActionOnConditionController()
{
_action = NULL;
_condition = NULL;
_finished = true;
}
~ActionOnConditionController()
{
if(_action != NULL)
_action.Release();
if(_condition != NULL)
_condition.Release();
}
bool Set(IAction* action, ICondition *condition)
{
if(!_finished || action == NULL)
return false;
if(_action != NULL)
_action.Release();
_action = action;
_action.AddRef();
_finished = false;
if(_condition != NULL)
_condition.Release();
_condition = condition;
_condition.AddRef();
return true;
}
void DoLogic(const int period, const datetime date)
{
if(_finished)
return;
if(_condition.IsPass(period, date))
{
if(_action.DoAction(period, date))
_finished = true;
}
}
};
#endif
#ifndef ActionOnConditionLogic_IMP
#define ActionOnConditionLogic_IMP
class ActionOnConditionLogic
{
ActionOnConditionController* _controllers[];
public:
~ActionOnConditionLogic()
{
int count = ArraySize(_controllers);
for(int i = 0; i < count; ++i)
{
delete _controllers[i];
}
}
void DoLogic(const int period, const datetime date)
{
int count = ArraySize(_controllers);
for(int i = 0; i < count; ++i)
{
_controllers[i].DoLogic(period, date);
}
}
bool AddActionOnCondition(IAction* action, ICondition* condition)
{
int count = ArraySize(_controllers);
for(int i = 0; i < count; ++i)
{
if(_controllers[i].Set(action, condition))
return true;
}
ArrayResize(_controllers, count + 1);
_controllers[count] = new ActionOnConditionController();
return _controllers[count].Set(action, condition);
}
};
#endif
#ifndef MarketOrderBuilder_IMP
#define MarketOrderBuilder_IMP
class MarketOrderBuilder
{
OrderSide _orderSide;
string _instrument;
double _amount;
double _rate;
int _slippage;
double _stop;
double _limit;
int _magicNumber;
string _comment;
bool _ecnBroker;
ActionOnConditionLogic* _actions;
public:
MarketOrderBuilder(ActionOnConditionLogic* actions)
{
_ecnBroker = false;
_actions = actions;
_amount = 0;
_rate = 0;
_slippage = 0;
_stop = 0;
_limit = 0;
_magicNumber = 0;
}
MarketOrderBuilder* SetECNBroker(bool isEcn) { _ecnBroker = isEcn; return &this; }
MarketOrderBuilder* SetComment(const string comment) { _comment = comment; return &this; }
MarketOrderBuilder* SetSide(const OrderSide orderSide) { _orderSide = orderSide; return &this; }
MarketOrderBuilder* SetInstrument(const string instrument) { _instrument = instrument; return &this; }
MarketOrderBuilder* SetAmount(const double amount) { _amount = amount; return &this; }
MarketOrderBuilder* SetSlippage(const int slippage) { _slippage = slippage; return &this; }
MarketOrderBuilder* SetStopLoss(const double stop) { _stop = stop; return &this; }
MarketOrderBuilder* SetTakeProfit(const double limit) { _limit = limit; return &this; }
MarketOrderBuilder* SetMagicNumber(const int magicNumber) { _magicNumber = magicNumber; return &this; }
ulong Execute(string &error)
{
int tradeMode = (int)SymbolInfoInteger(_instrument, SYMBOL_TRADE_MODE);
switch(tradeMode)
{
case SYMBOL_TRADE_MODE_DISABLED:
error = "Trading is disbled";
return 0;
case SYMBOL_TRADE_MODE_CLOSEONLY:
error = "Only close is allowed";
return 0;
case SYMBOL_TRADE_MODE_SHORTONLY:
if(_orderSide == BuySide)
{
error = "Only short are allowed";
return 0;
}
break;
case SYMBOL_TRADE_MODE_LONGONLY:
if(_orderSide == SellSide)
{
error = "Only long are allowed";
return 0;
}
break;
}
ENUM_ORDER_TYPE orderType = _orderSide == BuySide ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
int digits = (int)SymbolInfoInteger(_instrument, SYMBOL_DIGITS);
double rate = _orderSide == BuySide ? SymbolInfoDouble(_instrument, SYMBOL_ASK) : SymbolInfoDouble(_instrument, SYMBOL_BID);
double ticksize = SymbolInfoDouble(_instrument, SYMBOL_TRADE_TICK_SIZE);
MqlTradeRequest request;
ZeroMemory(request);
request.action = TRADE_ACTION_DEAL;
request.symbol = _instrument;
request.type = orderType;
request.volume = _amount;
request.price = MathRound(rate / ticksize) * ticksize;
request.deviation = _slippage;
request.sl = MathRound(_stop / ticksize) * ticksize;
request.tp = MathRound(_limit / ticksize) * ticksize;
request.magic = _magicNumber;
if(_comment != "")
request.comment = _comment;
request.type_filling = 0;
MqlTradeResult result;
ZeroMemory(result);
bool res = OrderSend(request, result);
switch(result.retcode)
{
case TRADE_RETCODE_INVALID_FILL:
error = "Invalid order filling type";
return 0;
case TRADE_RETCODE_LONG_ONLY:
error = "Only long trades are allowed for " + _instrument;
return 0;
case TRADE_RETCODE_INVALID_VOLUME:
{
double minVolume = SymbolInfoDouble(_instrument, SYMBOL_VOLUME_MIN);
error = "Invalid volume in the request. Min volume is: " + DoubleToString(minVolume);
}
return 0;
case TRADE_RETCODE_INVALID_PRICE:
error = "Invalid price in the request";
return 0;
case TRADE_RETCODE_INVALID_STOPS:
{
int filling = (int)SymbolInfoInteger(_instrument, SYMBOL_ORDER_MODE);
if((filling & SYMBOL_ORDER_SL) != SYMBOL_ORDER_SL)
{
error = "Stop loss in now allowed for " + _instrument;
return 0;
}
int minStopDistancePoints = (int)SymbolInfoInteger(_instrument, SYMBOL_TRADE_STOPS_LEVEL);
double point = SymbolInfoDouble(_instrument, SYMBOL_POINT);
double price = request.stoplimit > 0.0 ? request.stoplimit : request.price;
if(MathRound(MathAbs(price - request.sl) / point) < minStopDistancePoints)
{
error = "Your stop level is too close. The minimal distance allowed is " + IntegerToString(minStopDistancePoints) + " points";
}
else
{
error = "Invalid stops in the request";
}
}
return 0;
case TRADE_RETCODE_DONE:
break;
default:
error = "Unknown error: " + IntegerToString(result.retcode);
return 0;
}
return result.order;
}
};
#endif
#ifndef PositionSizeType_IMP
#define PositionSizeType_IMP
enum PositionSizeType
{
PositionSizeAmount, // $
PositionSizeContract, // In contracts
PositionSizeEquity, // % of equity
PositionSizeRisk, // Risk in % of equity
PositionSizeMoneyPerPip, // $ per pip
PositionSizeRiskCurrency // Risk in $
};
#endif
#ifndef StopLimitType_IMP
#define StopLimitType_IMP
enum StopLimitType
{
StopLimitDoNotUse, // Do not use
StopLimitPercent, // Set in %
StopLimitPips, // Set in Pips
StopLimitDollar, // Set in $,
StopLimitRiskReward, // Set in % of stop loss (take profit only)
StopLimitAbsolute // Set in absolite value (rate)
};
#endif
#ifndef InstrumentInfo_IMP
#define InstrumentInfo_IMP
class InstrumentInfo
{
string _symbol;
double _mult;
double _point;
double _pipSize;
int _digit;
double _ticksize;
public:
InstrumentInfo(const string symbol)
{
_symbol = symbol;
_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
_digit = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
_mult = _digit == 3 || _digit == 5 ? 10 : 1;
_pipSize = _point * _mult;
_ticksize = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE), _digit);
}
int CompareLots(double lot1, double lot2)
{
double lotStep = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_STEP);
if(lotStep == 0)
{
return lot1 < lot2 ? -1 : (lot1 > lot2 ? 1 : 0);
}
int lotSteps1 = (int)floor(lot1 / lotStep + 0.5);
int lotSteps2 = (int)floor(lot2 / lotStep + 0.5);
int res = lotSteps1 - lotSteps2;
return res;
}
static double GetPipSize(const string symbol)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double digit = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double mult = digit == 3 || digit == 5 ? 10 : 1;
return point * mult;
}
double GetPointSize() { return _point; }
double GetPipSize() { return _pipSize; }
int GetDigits() { return _digit; }
string GetSymbol() { return _symbol; }
static double GetBid(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_BID); }
static double GetAsk(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_ASK); }
double GetBid() { return SymbolInfoDouble(_symbol, SYMBOL_BID); }
double GetAsk() { return SymbolInfoDouble(_symbol, SYMBOL_ASK); }
double GetMinLots() { return SymbolInfoDouble(_symbol, SYMBOL_VOLUME_MIN); };
double RoundRate(const double rate)
{
return NormalizeDouble(MathRound(rate / _ticksize) * _ticksize, _digit);
}
double RoundLots(const double lots)
{
double lotStep = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_STEP);
if(lotStep == 0)
{
return 0.0;
}
return floor(lots / lotStep) * lotStep;
}
double LimitLots(const double lots)
{
double minVolume = GetMinLots();
if(minVolume > lots)
{
return 0.0;
}
double maxVolume = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_MAX);
if(maxVolume < lots)
{
return maxVolume;
}
return lots;
}
double NormalizeLots(const double lots)
{
return LimitLots(RoundLots(lots));
}
};
#endif
#ifndef CompareType_IMP
#define CompareType_IMP
enum CompareType
{
CompareLessThan
};
#endif
#ifndef TradesIterator_IMP
class TradesIterator
{
bool _useMagicNumber;
int _magicNumber;
int _orderType;
bool _useSide;
bool _isBuySide;
int _lastIndex;
bool _useSymbol;
string _symbol;
bool _useProfit;
double _profit;
CompareType _profitCompare;
string _comment;
public:
TradesIterator()
{
_comment = NULL;
_useMagicNumber = false;
_useSide = false;
_lastIndex = INT_MIN;
_useSymbol = false;
_useProfit = false;
}
TradesIterator* WhenComment(string comment)
{
_comment = comment;
return &this;
}
void WhenSymbol(const string symbol)
{
_useSymbol = true;
_symbol = symbol;
}
void WhenProfit(const double profit, const CompareType compare)
{
_useProfit = true;
_profit = profit;
_profitCompare = compare;
}
void WhenSide(const bool isBuy)
{
_useSide = true;
_isBuySide = isBuy;
}
void WhenMagicNumber(const int magicNumber)
{
_useMagicNumber = true;
_magicNumber = magicNumber;
}
ulong GetTicket() { return PositionGetTicket(_lastIndex); }
double GetLots() { return PositionGetDouble(POSITION_VOLUME); }
double GetSwap() { return PositionGetDouble(POSITION_SWAP); }
double GetProfit() { return PositionGetDouble(POSITION_PROFIT); }
double GetOpenPrice() { return PositionGetDouble(POSITION_PRICE_OPEN); }
double GetStopLoss() { return PositionGetDouble(POSITION_SL); }
double GetTakeProfit() { return PositionGetDouble(POSITION_TP); }
ENUM_POSITION_TYPE GetPositionType() { return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); }
bool IsBuyOrder() { return GetPositionType() == POSITION_TYPE_BUY; }
string GetSymbol() { return PositionGetSymbol(_lastIndex); }
int Count()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket) && PassFilter(i))
{
count++;
}
}
return count;
}
bool Next()
{
if(_lastIndex == INT_MIN)
{
_lastIndex = PositionsTotal() - 1;
}
else
_lastIndex = _lastIndex - 1;
while(_lastIndex >= 0)
{
ulong ticket = PositionGetTicket(_lastIndex);
if(PositionSelectByTicket(ticket) && PassFilter(_lastIndex))
return true;
_lastIndex = _lastIndex - 1;
}
return false;
}
bool Any()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket) && PassFilter(i))
{
return true;
}
}
return false;
}
ulong First()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket) && PassFilter(i))
{
return ticket;
}
}
return 0;
}
private:
bool PassFilter(const int index)
{
if(_useMagicNumber && PositionGetInteger(POSITION_MAGIC) != _magicNumber)
return false;
if(_useSymbol && PositionGetSymbol(index) != _symbol)
return false;
if(_useProfit)
{
switch(_profitCompare)
{
case CompareLessThan:
if(PositionGetDouble(POSITION_PROFIT) >= _profit)
return false;
break;
}
}
if(_useSide)
{
ENUM_POSITION_TYPE positionType = GetPositionType();
if(_isBuySide && positionType != POSITION_TYPE_BUY)
return false;
if(!_isBuySide && positionType != POSITION_TYPE_SELL)
return false;
}
if(_comment != NULL)
{
if(_comment != PositionGetString(POSITION_COMMENT))
return false;
}
return true;
}
};
#define TradesIterator_IMP
#endif
#ifndef TradingCalculator_IMP
#define TradingCalculator_IMP
class TradingCalculator
{
InstrumentInfo *_symbolInfo;
public:
static TradingCalculator* Create(string symbol)
{
return new TradingCalculator(symbol);
}
TradingCalculator(const string symbol)
{
_symbolInfo = new InstrumentInfo(symbol);
}
~TradingCalculator()
{
delete _symbolInfo;
}
InstrumentInfo *GetSymbolInfo()
{
return _symbolInfo;
}
double GetBreakevenPrice(const bool isBuy, const int magicNumber)
{
string symbol = _symbolInfo.GetSymbol();
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double price = isBuy ? _symbolInfo.GetBid() : _symbolInfo.GetAsk();
double totalPL = 0;
double totalAmount = 0;
TradesIterator it1();
it1.WhenMagicNumber(magicNumber);
it1.WhenSymbol(symbol);
it1.WhenSide(isBuy);
while(it1.Next())
{
double orderLots = PositionGetDouble(POSITION_VOLUME);
totalAmount += orderLots / lotStep;
double openPrice = it1.GetOpenPrice();
if(isBuy)
totalPL += (price - openPrice) * (orderLots / lotStep);
else
totalPL += (openPrice - price) * (orderLots / lotStep);
}
if(totalAmount == 0.0)
return 0.0;
double shift = -(totalPL / totalAmount);
return isBuy ? price + shift : price - shift;
}
double CalculateTakeProfit(const bool isBuy, const double takeProfit, const StopLimitType takeProfitType, const double amount, double basePrice)
{
int direction = isBuy ? 1 : -1;
switch(takeProfitType)
{
case StopLimitPercent:
return basePrice + basePrice * takeProfit / 100.0 * direction;
case StopLimitPips:
return basePrice + takeProfit * _symbolInfo.GetPipSize() * direction;
case StopLimitDollar:
return basePrice + CalculateSLShift(amount, takeProfit) * direction;
}
return 0.0;
}
double CalculateStopLoss(const bool isBuy, const double stopLoss, const StopLimitType stopLossType, const double amount, double basePrice)
{
int direction = isBuy ? 1 : -1;
switch(stopLossType)
{
case StopLimitPercent:
return basePrice - basePrice * stopLoss / 100.0 * direction;
case StopLimitPips:
return basePrice - stopLoss * _symbolInfo.GetPipSize() * direction;
case StopLimitDollar:
return basePrice - CalculateSLShift(amount, stopLoss) * direction;
}
return 0.0;
}
double GetLots(PositionSizeType lotsType, double lotsValue, const OrderSide orderSide, const double price, double stopDistance)
{
switch(lotsType)
{
case PositionSizeMoneyPerPip:
{
double unitCost = SymbolInfoDouble(_symbolInfo.GetSymbol(), SYMBOL_TRADE_TICK_VALUE);
double mult = _symbolInfo.GetPipSize() / _symbolInfo.GetPointSize();
double lots = RoundLots(lotsValue / (unitCost * mult));
return LimitLots(lots);
}
case PositionSizeAmount:
return GetLotsForMoney(orderSide, price, lotsValue);
case PositionSizeContract:
return LimitLots(RoundLots(lotsValue));
case PositionSizeEquity:
return GetLotsForMoney(orderSide, price, AccountInfoDouble(ACCOUNT_EQUITY) * lotsValue / 100.0);
case PositionSizeRisk:
{
double affordableLoss = AccountInfoDouble(ACCOUNT_EQUITY) * lotsValue / 100.0;
double unitCost = SymbolInfoDouble(_symbolInfo.GetSymbol(), SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_symbolInfo.GetSymbol(), SYMBOL_TRADE_TICK_SIZE);
double possibleLoss = unitCost * stopDistance / tickSize;
if(possibleLoss <= 0.01)
return 0;
return LimitLots(RoundLots(affordableLoss / possibleLoss));
}
}
return lotsValue;
}
bool IsLotsValid(const double lots, PositionSizeType lotsType, string &error)
{
switch(lotsType)
{
case PositionSizeContract:
return IsContractLotsValid(lots, error);
}
return true;
}
double NormilizeLots(double lots)
{
return LimitLots(RoundLots(lots));
}
private:
bool IsContractLotsValid(const double lots, string &error)
{
double minVolume = SymbolInfoDouble(_symbolInfo.GetSymbol(), SYMBOL_VOLUME_MIN);
if(minVolume > lots)
{
error = "Min. allowed lot size is " + DoubleToString(minVolume);
return false;
}
double maxVolume = SymbolInfoDouble(_symbolInfo.GetSymbol(), SYMBOL_VOLUME_MAX);
if(maxVolume < lots)
{
error = "Max. allowed lot size is " + DoubleToString(maxVolume);
return false;
}
return true;
}
double GetLotsForMoney(const OrderSide orderSide, const double price, const double money)
{
ENUM_ORDER_TYPE orderПоказано первые 50% кода (символов: 31483)
Открыть по тарифу
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
Разовая покупка файла
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.