class COrderData
Класс, представляющий структуру данных отдельного ордера: тикет, символ, тип, лот, цены открытия и закрытия, стоп-лосс, тейк-профит, время, магик, комментарий и состояние.
Сообщите нам, если считаете, что материал размещён без разрешения.
Обращение по товару «ordersevents.mqh» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
Библиотека ordersevents.mqh предназначена для работы с данными ордеров и позиций в среде MetaTrader 4. Она обеспечивает загрузку, сохранение и синхронизацию информации о торговых операциях между мастер- и слейв-счетами через CSV-файлы.
Класс COrderData представляет структуру данных отдельного ордера, включая тикет, символ, тип, лот, цены, время и комментарий. Класс COrderEvents управляет массивами ордеров и позиций, предоставляет методы загрузки и сохранения данных, а также набор виртуальных методов-обработчиков для реагирования на изменения.
Библиотека поддерживает автоматическое отслеживание изменений в позициях, ордерах и истории сделок, включая частичное закрытие. Данные о состоянии торгового счета сохраняются в файлы для последующей передачи на клиентские терминалы.
Классов: 2 · методов: 38 · конструкторов: 2 · деструкторов: 1 · отдельных функций: 0
class COrderData
Класс, представляющий структуру данных отдельного ордера: тикет, символ, тип, лот, цены открытия и закрытия, стоп-лосс, тейк-профит, время, магик, комментарий и состояние.
class COrderEvents
Основной класс библиотеки, управляющий массивами ордеров и позиций, загрузкой и сохранением данных, а также синхронизацией между мастер- и слейв-счетами.
void Reset()
Сбрасывает все поля объекта-ордера в значения по умолчанию.
COrderData()
Конструктор класса, создающий объект ордера с инициализацией значений по умолчанию через метод Reset.
void operator=(const int nil)
Присваивание нулевой инициализации объекту ордера (сброс всех полей).
void operator=(const COrderData &data)
Копирование данных одного объекта ордера в другой.
bool Load(long laccount,string &sdata[],long time_offset)
Загрузка данных ордера из строкового массива с парсингом полей из CSV-формата.
long GetMagic(long iaccount)
Возвращает магический номер ордера для указанного аккаунта.
void Print()
Выводит информацию об ордере в журнал для отладки.
void Reset()
Сбрасывает все внутренние поля и массивы объекта в начальные значения.
COrderEvents()
Конструктор класса, создающий объект с инициализацией через метод Reset.
~COrderEvents()
Деструктор, вызывающий деинициализацию и освобождение массивов.
bool do_action_filedata_order(COrderData &order)
Виртуальный метод-обработчик, вызываемый при обработке данных ордера из файла; по умолчанию возвращает true.
void do_event_init()
Виртуальный метод, вызываемый при инициализации событий; используется для пользовательской инициализации.
void OnInit(string sversion,string sCopierPath,string sFilePrefix)
Инициализирует версию, путь и префикс файла, сбрасывает состояние и вызывает do_event_init.
void OnDeinit(const int reason)
Вызывается при деинициализации для освобождения массивов ордеров и позиций.
void FilterComment(string &comment)
Заменяет запрещённый символ-разделитель (точку с запятой) в комментарии ордера на двоеточие.
ulong ConvertTicket(ulong thist)
Преобразует тикет, очищая старший бит (специфика MQL4).
bool do_filter_filedata_update(bool bupdate)
Виртуальный фильтр, определяющий необходимость обновления данных; по умолчанию пропускает запрос без изменений.
void do_event_change_type(CPositionInfo &tmp)
Виртуальный обработчик изменения типа позиции (CPositionInfo).
void do_event_change_volume(CPositionInfo &tmp)
Виртуальный обработчик изменения объёма позиции (CPositionInfo).
void do_event_change_open(CPositionInfo &tmp)
Виртуальный обработчик изменения цены открытия позиции (CPositionInfo).
void do_event_change_stop(CPositionInfo &tmp)
Виртуальный обработчик изменения стоп-лосса позиции (CPositionInfo).
void do_event_change_take(CPositionInfo &tmp)
Виртуальный обработчик изменения тейк-профита позиции (CPositionInfo).
void do_event_change_type(COrderInfo &tmp)
Виртуальный обработчик изменения типа ордера (COrderInfo).
void do_event_change_volume(COrderInfo &tmp)
Виртуальный обработчик изменения объёма ордера (COrderInfo).
void do_event_change_open(COrderInfo &tmp)
Виртуальный обработчик изменения цены открытия ордера (COrderInfo).
void do_event_change_stop(COrderInfo &tmp)
Виртуальный обработчик изменения стоп-лосса ордера (COrderInfo).
void do_event_change_take(COrderInfo &tmp)
Виртуальный обработчик изменения тейк-профита ордера (COrderInfo).
void do_event_change_state(COrderInfo &tmp)
Виртуальный обработчик изменения состояния ордера (COrderInfo).
void do_event_change_type(CHistoryOrderInfo &tmp)
Виртуальный обработчик изменения типа исторического ордера (CHistoryOrderInfo).
void do_event_change_close(CHistoryOrderInfo &tmp)
Виртуальный обработчик закрытия исторического ордера (CHistoryOrderInfo).
void do_action_loadfile_version(string &sdata[])
Виртуальный обработчик, вызываемый при загрузке данных версии из файла.
void do_action_loadfile_account(string &sdata[])
Виртуальный обработчик, вызываемый при загрузке данных аккаунта из файла.
void do_action_loadfile_position(string &sdata[])
Виртуальный обработчик, вызываемый при загрузке данных позиции из файла.
void Master(long imaster_account)
Настраивает мастер-аккаунт, определяет режим счета (хедж/фикс), обновляет массивы ордеров и позиций из текущего состояния терминала.
void Slave(ulong iAccountNumber)
Загружает данные с указанного слейв-счёта из CSV-файла, проверяя актуальность по времени последнего обновления.
void ClearTicket(ulong tclear)
Удаляет ордер из массива по указанному тикету, если он уже закрыт.
void Save()
Сохраняет текущие данные об ордерах и позициях в CSV-файл мастер-аккаунта с учётом смещения времени.
int LoadFile(string sfile,ulong iAccountNumber)
Загружает и парсит данные из CSV-файла слейв-аккаунта, заполняя массивы ордеров и позиций; поддерживает версию, аккаунт и позиционные данные.
enum_type_data GetData(string &sstring,string &sdata[])
Определяет тип данных строки (версия, аккаунт или позиция) и разбивает её на массив значений.
bool NettingPosition(COrderData &data)
Управляет расчётом лота для неттинг-счёта при добавлении нового ордера, суммируя объёмы по символу.
double NettingPositionMaster(string sSymbol,ENUM_ORDER_TYPE &etype)
Возвращает суммарный лот и направление позиции мастера по указанному символу для неттинг-счёта.
#ifndef __SOVETNIKOV_ORDEREVENTS__
#define __SOVETNIKOV_ORDEREVENTS__ ""
#include <SovetnikovNET/Copier.mqh>
class COrderData
{
public:
ulong master_ticket; //тикет на мастере
ulong first_ticket;
string master_symbol; //имя инструмента
int type; //4 тип
double lot; //8 лот
double lotout; //8 Лот закрытый
double open; //8 цена открытие ордера
double close; //8 цена закрытие ордера
double stop; //8 стоп
double take; //8 тейк
datetime tmOpen;//8 открытие
datetime tmClose;//8 закрытие
long magic; //4 меджик - на клиенте тут формируется (int)(master_ticket+account), если это мт5, то там формировать будет (long)(master_ticket+account)
double real_open; //реальная цена открытия на клиенте
long master_magic; //меджик мастера (при загрузке на клиенте данных от мастера) mt5
string comment;
long state;
bool history;
void Reset()
{
master_ticket=0; //тикет на мастере
first_ticket=0;
master_symbol=""; //имя инструмента
type=0; //4 тип
lot=0; //8 лот
lotout=0; //8 лот закрытый
open=0; //8 цена открытие ордера
real_open=0; //реальная цена открытия на слейв
close=0; //8 цена закрытие ордера
stop=0; //8 стоп
take=0; //8 тейк
tmOpen=0;//8 открытие
tmClose=0;//8 закрытие
magic=0; //4 меджик - у мастера тут свой меджик, на клиенте тут храним меджик для контроля ордера
comment="";
state=0;
history=false; //открытый ордер по умолчанию
}
COrderData()
{
Reset();
}
void operator=(const int nil=NULL) //нулевая инициализация переменных
{
Reset();
}
void operator=(const COrderData &data)//простое копирование
{
master_ticket=data.master_ticket; //тикет на мастере
first_ticket=data.first_ticket;
master_symbol=data.master_symbol; //имя инструмента
type=data.type; //4 тип
lot=data.lot; //8 лот
lotout=data.lotout;
open=data.open; //8 цена открытие ордера
real_open=data.real_open;
close=data.close; //8 цена закрытие ордера
stop=data.stop; //8 стоп
take=data.take; //8 тейк
tmOpen=data.tmOpen;//8 открытие
tmClose=data.tmClose;//8 закрытие
magic=data.magic; //4 меджик (будем такой же ставить)
comment=data.comment;
state=data.state;
history=data.history;
}
bool Load(long laccount,string &sdata[],long time_offset)
{
if(ArraySize(sdata)==16)
{
/*//по умолчанию точность double 6 цифр после запятой, сделал поддержку до 8 цифр на случай появления криптовалют с такой точностью
swrite=StringFormat("%s;%lld;%lld;%s;%d;%.8f;%.8f;%.8f;%.8f;%.8f;%lld;%lld;%lld;%ls;%d\n\r",
orders[i].history?"closed":"open",//0
orders[i].master_ticket,//1
orders[i].first_ticket,//2
orders[i].master_symbol,//3
orders[i].type,//4
orders[i].lot,//5
orders[i].open,//6
orders[i].close,//7
orders[i].stop,//8
orders[i].take,//9
orders[i].tmOpen>0?orders[i].tmOpen:0,//10
orders[i].tmClose>0?orders[i].tmClose:0,//11
orders[i].magic,//12
orders[i].comment,//13
orders[i].state);//14
*/
history=false;
if(sdata[0]=="closed")
history=true;
master_ticket=StringToInteger(sdata[1]);
first_ticket=StringToInteger(sdata[2]); //эти данные мастер заполняет если было частичное закрытие и тут будет ордер первый до частичного закрытия
master_symbol=sdata[3];
type=(int)StringToInteger(sdata[4]);
lot=StringToDouble(sdata[5]);
lotout=StringToDouble(sdata[6]);
open=StringToDouble(sdata[7]);
close=StringToDouble(sdata[8]);
stop=StringToDouble(sdata[9]);
take=StringToDouble(sdata[10]);
tmOpen=(datetime)StringToInteger(sdata[11]);
tmClose=(datetime)StringToInteger(sdata[12]);
/*if(tmOpen>0)
tmOpen=(datetime)(tmOpen-ltime_offset);
if(tmClose>0)
tmClose=(datetime)(tmClose-ltime_offset);
*/
#ifdef __MQL5__
master_magic=StringToInteger(sdata[13]); //сохраним на будущее меджик с мастера
magic=(ulong)(master_magic-laccount);//mt5
#else
master_magic=int(StringToInteger(sdata[13])); //сохраним на будущее меджик с мастера
magic=(int)(master_magic-laccount);//mt5
#endif
comment=sdata[14];
state=StringToInteger(sdata[15]);
return true; //загрузка успешная
}
return false; //неверные данные
}
long GetMagic(long iaccount)
{
return magic;
}
void Print()
{
if(master_ticket<=0)
return;
PrintFormat("Print: %s;%lld;%lld;Master=%s;%d;%.8f;%.8f;%.8f;%.8f;%.8f;%.8f;%.8f;%s;%s;%lld;%ls;%d",
history?"closed":"open",//0
master_ticket,//1
first_ticket,//2
master_symbol,//3
type,//4
lot,//5
lotout,//6
open,//7
real_open, //real open
close,//8
stop,//9
take,//10
tmOpen>0?TimeToString(tmOpen,TIME_DATE|TIME_MINUTES|TIME_SECONDS):"",//11
tmClose>0?TimeToString(tmClose,TIME_DATE|TIME_MINUTES|TIME_SECONDS):"",//12
magic,//13
comment,//14
state);//15
}
};
class COrderEvents
{
public:
int ierror;
long ilasttotal;
bool is_hedge,is_fifo; //тип счета неттинг или хедж
datetime time;
bool bRestart; //при рестарте ставим флаг true для сохранения файла даже без сделок
ulong lastupdate; //последнее обновлене тик таймер
bool is_save; //возможность выключить сохранение в файл
COrderData orders[];//ордера открытые и закрытые
COrderData position[]; //для нет счета
bool bLoad; //флаг успешной загрузки данных
ulong m_master_account;
string sCopirName,sfile_prefix,sVersion;
ulong tm_last_open_file;
long time_offset;
virtual void Reset()
{
is_hedge=false;
is_fifo=false;
lastupdate=0;
is_save=true;
ilasttotal=0;
bLoad=false;
tm_last_open_file=0;
ArrayResize(orders,0,500);
ArrayResize(position,0,500);
ierror=0;
sVersion="1.00";
time_offset=0;
sfile_prefix="";
m_master_account=0;
}
COrderEvents()
{
Reset();
}
~COrderEvents()
{
this.OnDeinit();
}
virtual bool do_action_filedata_order(COrderData &order)
{
return true;
}
virtual void do_event_init()
{
}
virtual void OnInit(string sversion,string sCopierPath,string sFilePrefix)
{
// код доступен только после оплаты
}
virtual void OnDeinit(const int reason=0)
{
ArrayFree(orders);
ArrayFree(position);
}
void FilterComment(string &comment)
{
StringReplace(comment,";",":"); //запрещенный символ, его заменяем т.к. используется для разделителя
}
#ifdef __MQL4__
ulong ConvertTicket(ulong thist)
{
if((thist & 0x8000000000000000) != 0)
{
thist &=~0x8000000000000000;
}
return thist;
}
#else
ulong ConvertTicket(ulong thist)
{
return thist;
}
#endif
virtual bool do_filter_filedata_update(bool bupdate)
{
return bupdate;
}
/*
обработчики виртуальные для работы с изменениями позиций, ордеров и закрытых\отмененных
*/
virtual void do_event_change_type(CPositionInfo &tmp) {}
virtual void do_event_change_volume(CPositionInfo &tmp) {}
virtual void do_event_change_open(CPositionInfo &tmp) {}
virtual void do_event_change_stop(CPositionInfo &tmp) {}
virtual void do_event_change_take(CPositionInfo &tmp) {}
virtual void do_event_change_type(COrderInfo &tmp) {}
virtual void do_event_change_volume(COrderInfo &tmp) {}
virtual void do_event_change_open(COrderInfo &tmp) {}
virtual void do_event_change_stop(COrderInfo &tmp) {}
virtual void do_event_change_take(COrderInfo &tmp) {}
virtual void do_event_change_state(COrderInfo &tmp) {}
virtual void do_event_change_type(CHistoryOrderInfo &tmp) {}
virtual void do_event_change_close(CHistoryOrderInfo &tmp) {}
virtual void do_action_loadfile_version(string &sdata[]) {}
virtual void do_action_loadfile_account(string &sdata[]) {}
virtual void do_action_loadfile_position(string &sdata[]) {}
virtual void Master(long imaster_account)
{
m_master_account=imaster_account;
#ifdef __MQL5__
is_hedge=AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;
is_fifo=AccountInfoInteger(ACCOUNT_FIFO_CLOSE);
#else
is_hedge=true;
is_fifo=false;
#endif
lastupdate=GetMicrosecondCount();
int isize=ArraySize(orders);
CPositionInfo pos;
COrderInfo ord;
CHistoryOrderInfo hist;
bool bUpdate=false; //если структура пустая, то обновляем ее
int jorders=0;
for(int i=isize-1; i>=0; i--)
{
if(orders[i].master_ticket==0)
{
continue;
}
bool bFind=false;
if(!bUpdate && orders[i].history==false && pos.SelectByTicket(orders[i].master_ticket))
{
int iDigit=(int)SymbolInfoInteger(pos.Symbol(),SYMBOL_DIGITS);
int iLotDigits=LotDigits(pos.Symbol());
if(orders[i].type!=pos.PositionType())
{
bUpdate=true;
do_event_change_type(pos);
}
if(NormalizeDouble(orders[i].lot,iLotDigits)!=NormalizeDouble(pos.Volume(),iLotDigits))
{
bUpdate=true;
do_event_change_volume(pos);
}
if(NormalizeDouble(orders[i].open,iDigit)!=NormalizeDouble(pos.PriceOpen(),iDigit))
{
bUpdate=true;
do_event_change_open(pos);
}
if(NormalizeDouble(orders[i].stop,iDigit)!=NormalizeDouble(pos.StopLoss(),iDigit))
{
bUpdate=true;
do_event_change_stop(pos);
}
if(NormalizeDouble(orders[i].take,iDigit)!=NormalizeDouble(pos.TakeProfit(),iDigit))
{
bUpdate=true;
do_event_change_take(pos);
}
bFind=true;
}
if(!bFind && !bUpdate && orders[i].history==false && ord.Select(orders[i].master_ticket))
{
int iDigit=(int)SymbolInfoInteger(ord.Symbol(),SYMBOL_DIGITS);
int iLotDigits=LotDigits(ord.Symbol());
if(orders[i].type!=ord.OrderType())
{
bUpdate=true;
do_event_change_type(ord);
}
if(NormalizeDouble(orders[i].lot,iLotDigits)!=NormalizeDouble(ord.VolumeInitial(),iLotDigits))
{
bUpdate=true;
do_event_change_volume(ord);
}
if(NormalizeDouble(orders[i].open,iDigit)!=NormalizeDouble(ord.PriceOpen(),iDigit))
{
bUpdate=true;
do_event_change_open(ord);
}
if(NormalizeDouble(orders[i].stop,iDigit)!=NormalizeDouble(ord.StopLoss(),iDigit))
{
bUpdate=true;
do_event_change_stop(ord);
}
if(NormalizeDouble(orders[i].take,iDigit)!=NormalizeDouble(ord.TakeProfit(),iDigit))
{
bUpdate=true;
do_event_change_take(ord);
}
if(orders[i].state!=ord.State())
{
bUpdate=true;
do_event_change_state(ord);
}
bFind=true;
}
if(!bUpdate && !bFind && orders[i].history==false)
{
ulong iticket=orders[i].first_ticket>0?orders[i].first_ticket:orders[i].master_ticket;
#ifdef __MQL4__
if(OrderSelect((int)iticket,SELECT_BY_TICKET))
#else
if(HistoryOrderSelect(iticket))
#endif
{
#ifdef __MQL4__
if(::OrderCloseTime()==0)
{
orders[i].history=false;
continue;
}
#endif
hist.Ticket(iticket);
if(orders[i].type!=hist.OrderType())
{
bUpdate=true;
do_event_change_type(hist);
}
if(orders[i].tmClose!=hist.TimeDone())
{
bUpdate=true;
do_event_change_close(hist);
}
bFind=true;
}
}
if(bUpdate)
break; //если хоть один ордер нужно обновить, то обновим и не будем првоерять остальные
}
bUpdate=do_filter_filedata_update(bUpdate);
if(!bUpdate)
{
HistorySelect(0,TimeCurrent());
#ifdef __MQL5__
long itotal=HistoryDealsTotal()+PositionsTotal()+OrdersTotal();
#else
long itotal=HistoryOrdersTotal()+OrdersTotal();
#endif
if(itotal!=ilasttotal)
{
if(ilasttotal!=0)
Print("Update data, last count: ",ilasttotal," new count:",itotal);
bUpdate=true;
ilasttotal=itotal;
}
}
if(bUpdate || bRestart)
{
bRestart=false;
ArrayFree(orders);
isize=0;
#ifdef __MQL5__
for(int i=PositionsTotal()-1; i>=0; i--)
#else
for(int i=OrdersTotal()-1; i>=0; i--)
#endif
{
if(!pos.SelectByIndex(i))
continue;
if(pos.PositionType()>=2)
continue; //проверим по типу, пригодиться для мт4
isize=ArraySize(orders);
if(ArrayResize(orders,isize+1)!=-1)
{
orders[isize].history=false;
orders[isize].master_ticket=pos.Ticket();
orders[isize].master_symbol=pos.Symbol();
orders[isize].magic=pos.Magic();
orders[isize].lot=pos.Volume();
orders[isize].lotout=0; //сюда надо будет записать значение объема на который были закрыты сделки
orders[isize].type=pos.PositionType();
orders[isize].open=pos.PriceOpen();
orders[isize].stop=pos.StopLoss();
orders[isize].take=pos.TakeProfit();
orders[isize].tmOpen=pos.Time();
orders[isize].tmClose=0;
orders[isize].comment=pos.Comment();
orders[isize].close=0;
orders[isize].state=0;
FilterComment(orders[isize].comment);
}
}
for(int i=OrdersTotal()-1; i>=0; i--)
{
if(!ord.SelectByIndex(i))
continue;
if(ord.OrderType()>=0 && (ord.OrderType()<2 || ord.OrderType()>=6))
continue; //првоерим по типу, пригодиться для мт4
isize=ArraySize(orders);
if(ArrayResize(orders,isize+1)!=-1)
{
orders[isize].history=false;
orders[isize].master_ticket=ord.Ticket();
orders[isize].master_symbol=ord.Symbol();
orders[isize].magic=ord.Magic();
orders[isize].lot=ord.VolumeCurrent();
orders[isize].lotout=0;
orders[isize].type=ord.OrderType();
orders[isize].open=ord.PriceOpen();
orders[isize].stop=ord.StopLoss();
orders[isize].take=ord.TakeProfit();
orders[isize].tmOpen=ord.TimeSetup();
orders[isize].tmClose=0;
orders[isize].comment=ord.Comment();
orders[isize].close=0;
orders[isize].state=ord.State();
FilterComment(orders[isize].comment);
}
}
datetime tmDay1=iTime(_Symbol,PERIOD_D1,3);
HistorySelect(tmDay1,TimeCurrent()); //загрузим историю за прошлый день, потребуется для отметки если был ордер закрыт
#ifdef __MQL5__
CDealInfo deal;
if(HistoryDealsTotal()>0)
{
for(int i=HistoryDealsTotal()-1; i>=0; i--)
{
if(!deal.SelectByIndex(i))
continue;
if(deal.Entry()==DEAL_ENTRY_INOUT || deal.Entry()==DEAL_ENTRY_OUT)
{
ulong tdeal=deal.Ticket();
long thist=deal.PositionId();
ENUM_DEAL_TYPE etype=deal.DealType();
if(etype==DEAL_TYPE_BUY || etype==DEAL_TYPE_SELL)
{
if(etype==DEAL_TYPE_BUY)
etype=DEAL_TYPE_SELL;
else
if(etype==DEAL_TYPE_SELL)
etype=DEAL_TYPE_BUY;
if(thist>0)
{
if(pos.SelectByTicket(ConvertTicket(thist)))
{
if(pos.Time()==0)
continue; //если этот оредр еще в рынке, то он не в истории!!
}
else
{
bool bfind=false;
for(int iisize=ArraySize(orders)-1; iisize>=0; iisize--)
{
if(orders[iisize].master_ticket==thist)
{
orders[iisize].history=true; //пометим что уже закрыт
bfind=true;
break;
}
}
if(!bfind && thist>0)
{
isize=ArraySize(orders);
if(ArrayResize(orders,isize+1)!=-1)
{
orders[isize].history=true;
orders[isize].master_ticket=thist;
orders[isize].master_symbol=deal.Symbol();
orders[isize].magic=deal.Magic();
orders[isize].type=etype;
orders[isize].stop=0;
orders[isize].take=0;
orders[isize].lot=deal.Volume();
orders[isize].lotout=0;
orders[isize].open=0;
orders[isize].close=deal.Price();
orders[isize].comment=deal.Comment();
orders[isize].state=4;
orders[isize].tmOpen=deal.Time();
orders[isize].tmClose=deal.Time();
HistorySelectByPosition(thist);
CDealInfo deal2;
double dsummlot=0.0;
double dlot=0.0,dlotout=0.0;
for(int j=0; j<HistoryDealsTotal(); j++)
{
if(!deal2.SelectByIndex(j))
continue;
Показано первые 50% кода (символов: 25120)
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.