oslib/osc/trade/osc_position.mqh

526 lines
56 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:15:18 +02:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| osc_position.mqh |
//| Copyright 2019, OS Corp |
//| http://www.os.org |
//| |
//| Versao 0.001 Mantem banco de dados de ordens e posicoes. |
//| 1. |
//| |
//| 2. |
//| |
//+------------------------------------------------------------------+
//
// Uma posicao tem:
// - um identificador
// - ordens de entrada
// - ordens de saida
// Uma ordem tem:
// - identificador
#property copyright "Copyright 2020, OS Corp."
#property link "http://www.os.org"
#property version "1.0"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Generic\HashMap.mqh>
#include <oslib\osc\trade\osc_ontradetransactionexport.mqh> // log para ontradetransaction
class OsOrdem : public CObject{
public:
int inOut ; // 1(in); -1(out) ; 0(desconhecida)
ulong idOrd ; // ticket da ordem
ulong idDea ; // ticket da negociacao;
ulong idPos ; // ticket da posicao a qual pertence a ordem;
ulong idToClose; // id deal da ordem que esta ordem estah fechando.
ulong mag ; // numero magico desta ordem;
ulong magMeClose; // numero magico da ordem de fechamento dessa ordem;
ulong magToClose; // numero magico da ordem que esta ordem estah fechando;
string symbol ; // ticker
double price ; // preco
double vol ; // volume
ENUM_TRADE_TRANSACTION_TYPE typTra; // ultimo tipo de transacao (evento) informado para a ordem
ENUM_DEAL_TYPE typDea;
ENUM_ORDER_TYPE typOrd;
ENUM_ORDER_STATE status;
ENUM_TRADE_REQUEST_ACTIONS reqAction; // acao solicitada na ultima requisicao
double reqPrice ; // preco da ultima requisicao
double reqVol ; // volume da ultima requisicao
ENUM_ORDER_TYPE reqTypOrd; // tipo de ordem feito na ultima requisicao
string reqComent; // comentario
uint retCode ; // codigo de retorno da ultima requisicao
ulong retOrder; // numero de ordem gerada, caso tenha sido colocada.
double retVol ; // volume da ultima gerada, confirmado pela corretora.
uint reqId ; // requist_id eh o identificador da requisicao. Eh definido pelo terminal durante a despacho da solicitacao.
//------------------------------------
// req.action , // ENUM_TRADE_REQUEST_ACTIONS // Tipo de opera<EFBFBD><EFBFBD>o de negocia<EFBFBD><EFBFBD>o
// req.magic , // ulong // Expert Advisor -conselheiro- ID (n<EFBFBD>mero m<EFBFBD>gico)
// req.order , // ulong // Bilhetagem da ordem
// req.symbol , // string // S<EFBFBD>mbolo de negocia<EFBFBD><EFBFBD>o
// req.volume , // double // Volume solicitado para uma encomenda em lotes
// req.price , // double // Pre<EFBFBD>o
// req.stoplimit , // double // N<EFBFBD>vel StopLimit da ordem
// req.sl , // double // N<EFBFBD>vel Stop Loss da ordem
// req.tp , // double // N<EFBFBD>vel Take Profit da ordem
// req.deviation , // ulong // M<EFBFBD>ximo desvio poss<EFBFBD>vel a partir do pre<EFBFBD>o requisitado
// req.type , // ENUM_ORDER_TYPE // Tipo de ordem
// req.type_filling , // ENUM_ORDER_TYPE_FILLING // Tipo de execu<EFBFBD><EFBFBD>o da ordem
// req.type_time , // ENUM_ORDER_TYPE_TIME // Tipo de expira<EFBFBD><EFBFBD>o da ordem
// req.expiration , // datetime // Hora de expira<EFBFBD><EFBFBD>o da ordem (para ordens do tipo ORDER_TIME_SPECIFIED))
// req.comment , // string // Coment<EFBFBD>rio sobre a ordem
// req.position , // ulong // Bilhete da posi<EFBFBD><EFBFBD>o
// req.position_by , // ulong // Bilhete para uma posi<EFBFBD><EFBFBD>o oposta
//------------------------------------
virtual int Compare( const CObject* node, // Node to compare with
const int mode=0){// Compare mode
if( this.mag > ((OsOrdem*)node).mag ) return 1;
if( this.mag < ((OsOrdem*)node).mag ) return -1;
return 0;
}
string toString(){
string str;
StringConcatenate(str
,"|ttr ",EnumToString(typTra)
,"|ord ",idOrd
,"|dea ",idDea
,"|pos ",idPos
,"|mag ",mag
,"|mml ",magMeClose
,"|mtc ",magToClose
,"|sym ",symbol
,"|io " ,inOut
,"|prc ",price
,"|vol ",vol
,"|tor ",EnumToString(typOrd)
,"|tde ",EnumToString(typDea)
,"|stt ",EnumToString(status)
);
return str;
}
};
/*
class CArrayOsOrdem : public CArrayObj{
public:
string toString(){
string str;
for(int i=0; i<Total(); i++){
StringConcatenate(str, str, ((OsOrdem*)At(i)).toString(),"\n" );
}
return str;
};
};
*/
class CHashOsOrdem : public CHashMap<ulong,OsOrdem*>{
public:
string toString(){
ulong chave[]; OsOrdem* valor[];
int qtd = CopyTo(chave,valor);
string str;
for(int i=0; i<qtd; i++){
StringConcatenate(str, str, valor[i].toString(),"\n" );
}
return str;
};
};
class osc_position{
private:
ulong m_idPos ; // id da posicao atual
double m_volIn ; // volume de contratos em trades IN.
double m_volOut ; // volume de contratos em trades OUT.
ENUM_DEAL_TYPE m_type ; // compra, venda ou desconhecida, conforme definidos em ENUM_TIPO_POSICAO.
string m_logPosicao; // string com os eventos ao longo da vida da posicao.
CHashOsOrdem m_ordens ; // hash com as ordens da posicao.
osc_ontradetransactionexport m_logTran; // loga transacoes da posicao em arqui csv.
void processarOrderAdd(const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res );
void processarOrderUpdate(const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res );
void processarOrderDelete(const MqlTradeTransaction& tran);
void processarTransactionRequest(const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res );
void processarDealAdd(const MqlTradeTransaction& tran ,
const MqlTradeRequest& req ,
const MqlTradeResult& res ,
bool& closer , // true: trade eh um fechamento de posicao
bool& toClose , // true: trade deve ser fechado
ulong& toCloseidDeal , // se toClose=true este serah o ticket do trade a ser fechado
double& toCloseVol , // se toClose=true este serah o volume do trade a ser fechado
ENUM_DEAL_TYPE& toCloseTypeDeal , // se toClose=true este serah o sentido do trade a ser fechado, conforme ENUM_DEAL_TYPE
double& toClosePriceIn , // se toClose=true este serah o preco do trade a ser fechado
bool& toCloseOpenPos );// se toClose=true esta indicarah se a posicao foi aberta agora (primeiraOrdem)
//------------------------- qtd microsegundos entre chamadas
ulong m_microsec_ant;
ulong m_microsec ;
ulong getMicrosec(){
m_microsec = GetMicrosecondCount()-m_microsec_ant;
m_microsec_ant += m_microsec;
return m_microsec;
}
//------------------------- auxiliar pra saber se comentario eh numerico (ordem de fechamento de posicao)
bool strEhNumero(string str, ulong &num){
num = StringToInteger(str);
return ( MathIsValidNumber(num) && num != 0 );
}
//-------------------------
public:
~osc_position(){ Print(m_logPosicao); }
void initialize();
bool initLogCSV (){ return m_logTran.initialize(); }
void deInitLogCSV(){ m_logTran.closeCSV (); }
uint logarInCSV( const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res ){ return m_logTran.printDetCSV(tran,req,res); }
void onTradeTransaction( const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res ,
bool& closer , // true: trade eh um fechamento de posicao
bool& toClose , // true: trade deve ser fechado
ulong& toCloseidDeal , // se toClose=true este serah o ticket do trade a ser fechado
double& toCloseVol , // se toClose=true este serah o volume do trade a ser fechado
ENUM_DEAL_TYPE& toCloseTypeDeal , // se toClose=true este serah o sentido do trade a ser fechado, conforme ENUM_DEAL_TYPE
double& toClosePriceIn , // se toClose=true este serah o preco do trade a ser fechado
bool& toCloseOpenPos ); // se toClose=true esta indicarah se a posicao foi aberta agora (primeiraOrdem)
bool getComment( const ulong ticket, string& comment ); //busca e retorna o comentario de uma ordem
};
void osc_position::initialize(void){
m_idPos = 0 ; // id da posicao atual
m_volIn = 0 ; // qtd trades IN multiplicada pelo volume.
m_volOut = 0 ; // qtd trades OUT multiplicada pelo volume.
m_type = DEAL_TYPE_COMMISSION; // na inicializacao nao usamos um tipo diferente de DEAL_TYPE_BUY ou DEAL_TYPE_SELL,
// pois ainda nao sabemos se eh posicao vendida ou comprada (as unicas que tratamos).
m_logPosicao = "" ; // string com os eventos ao longo da vida da posicao.
m_ordens.Clear(); ; // colecao com as ordens da posicao
}
void osc_position::processarOrderAdd(const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res ){
OsOrdem* ord = new OsOrdem ;
ord.idOrd = tran.order ;
//ord.idDea = tran.deal ;
//ord.idPos = tran.position ;
ord.typTra = tran.type ;
ord.typOrd = tran.order_type ;
//ord.typDea = tran.deal_type ;
ord.status = tran.order_state;
ord.symbol = tran.symbol ;
ord.price = tran.price ;
ord.vol = tran.volume;
m_ordens.Add(ord.idOrd, ord);
return;
}
void osc_position::processarOrderUpdate(const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res ){
// posicao vazia, adicione a ordem
OsOrdem* ord;
if( m_ordens.TryGetValue(tran.order, ord) ){
//ord.idOrd = tran.order ;
//ord.idDea = tran.deal ;
//ord.idPos = tran.position ;
ord.typTra = tran.type ;
ord.typOrd = tran.order_type ;
//ord.typDea = tran.deal_type ;
ord.status = tran.order_state;
ord.symbol = tran.symbol ;
ord.price = tran.price ;
ord.vol = tran.volume;
return;
}
// update de uma ordem nao registrada, incluimos agora...
processarOrderAdd(tran,req,res); //<TODO> ver como se comportarah se chegar apos a retirada de uma ordem da colecao.
}
// este evento ocorre quando uma ordem sai da lista de ordens pendentes
void osc_position::processarOrderDelete(const MqlTradeTransaction& tran){
// ordem que estah saindo da lista estah cancelada, entao retiramos do nosso banco de dados...
if( tran.order_state == ORDER_STATE_CANCELED ){ m_ordens.Remove(tran.order); }
}
void osc_position::processarTransactionRequest(const MqlTradeTransaction& tran,
const MqlTradeRequest& req ,
const MqlTradeResult& res ){
// nao processmos requisicoes de delecao de ordens.
// as ordens deletedas sao retiradas do nosso banco de dados (m_ordens) durante o
// processamento da transacao TRADE_TRANSACTION_ORDER_DELETE.
if(req.action == TRADE_ACTION_REMOVE) return;
// posicao vazia, adicione a ordem
OsOrdem* ord;
// Descobrindo qual a ordem da requisicao.
// Na primeira requisicao de uma ordem, seu numero fica no resultado, nas demais, fica na requisicao.
ulong order = req.order>0?req.order:res.order;
// se a ordem jah estiver neste controle, atualizamos suas informacoes de requisicao e resultado senao, a incluimos.
if( m_ordens.TryGetValue(order,ord) ){
// Como a ordem jah estava neste controle, soh atualizamos o tipo de transacao, caso esteja nulo.
if (ord.typTra == NULL ) ord.typTra = tran.type;
ord.reqAction = req.action ; // ENUM_TRADE_REQUEST_ACTIONS // Tipo de opera<EFBFBD><EFBFBD>o de negocia<EFBFBD><EFBFBD>o
ord.mag = req.magic ; // ulong // Expert Advisor -conselheiro- ID (n<EFBFBD>mero m<EFBFBD>gico)
// req.order ; // ulong // Bilhetagem da ordem
//ord.symbol = req.symbol ; // string // S<EFBFBD>mbolo de negocia<EFBFBD><EFBFBD>o
ord.reqVol = req.volume ; // double // Volume solicitado para uma encomenda em lotes
ord.reqPrice = req.price ; // double // Pre<EFBFBD>o
// req.stoplimit ; // double // N<EFBFBD>vel StopLimit da ordem
// req.sl ; // double // N<EFBFBD>vel Stop Loss da ordem
// req.tp ; // double // N<EFBFBD>vel Take Profit da ordem
// req.deviation ; // ulong // M<EFBFBD>ximo desvio poss<EFBFBD>vel a partir do pre<EFBFBD>o requisitado
//ord.reqTypOrd = req.type ; // ENUM_ORDER_TYPE // Tipo de ordem
// req.type_filling ; // ENUM_ORDER_TYPE_FILLING // Tipo de execu<EFBFBD><EFBFBD>o da ordem
// req.type_time ; // ENUM_ORDER_TYPE_TIME // Tipo de expira<EFBFBD><EFBFBD>o da ordem
// req.expiration ; // datetime // Hora de expira<EFBFBD><EFBFBD>o da ordem (para ordens do tipo ORDER_TIME_SPECIFIED))
ord.reqComent = req.comment ; // string // Coment<EFBFBD>rio sobre a ordem
// req.position ; // ulong // Bilhete da posi<EFBFBD><EFBFBD>o
// req.position_by ; // ulong // Bilhete para uma posi<EFBFBD><EFBFBD>o oposta
ord.retCode = res.retcode ;
ord.retOrder = res.order ;
ord.retVol = res.volume ;
ord.reqId = res.request_id ;
// eh um fechamento de posicao, gravamos no log pra checar a perfomance...
if( req.action==TRADE_ACTION_PENDING && strEhNumero(req.comment, ord.idToClose) ){
ord.inOut = -1;
StringConcatenate( m_logPosicao,m_logPosicao,"CLOSE_BY:",ord.idOrd,":",ord.inOut,":",EnumToString(ord.typOrd),":",ord.vol,":",ord.reqPrice,":",ord.idToClose,":",getMicrosec(),":"); // string com os eventos ao longo da vida da posicao.
}
return;
}
// ordem ainda nao estah no controle. Colocamos agora...
ord = new OsOrdem;
ord.typTra = tran.type ;
ord.idOrd = order ;
ord.reqAction = req.action ; // ENUM_TRADE_REQUEST_ACTIONS // Tipo de opera<EFBFBD><EFBFBD>o de negociacao
ord.mag = req.magic ; // ulong // Expert Advisor -conselheiro- ID (n<EFBFBD>mero magico)
ord.symbol = req.symbol ; // string // S<EFBFBD>mbolo de negociacao
ord.reqVol = req.volume ; // double // Volume solicitado para uma encomenda em lotes
ord.reqPrice = req.price ; // double // Preco
ord.reqTypOrd = req.type ; // ENUM_ORDER_TYPE // Tipo de ordem
ord.reqComent = req.comment ; // string // Comentario sobre a ordem
ord.retCode = res.retcode ;
ord.retOrder = res.order ;
ord.retVol = res.volume ;
ord.reqId = res.request_id;
m_ordens.Add(ord.idOrd, ord);
return;
}
void osc_position::processarDealAdd(const MqlTradeTransaction& tran ,
const MqlTradeRequest& req ,
const MqlTradeResult& res ,
bool& closer , // true: trade eh um fechamento de posicao
bool& toClose , // true: trade deve ser fechado
ulong& toCloseidDeal , // se toClose=true este serah o ticket do trade a ser fechado
double& toCloseVol , // se toClose=true este serah o volume do trade a ser fechado
ENUM_DEAL_TYPE& toCloseTypeDeal, // se toClose=true este serah o sentido do trade a ser fechado, conforme ENUM_DEAL_TYPE
double& toClosePriceIn , // se toClose=true este serah o preco do trade a ser fechado
bool& toCloseOpenPos ){ // se toClose=true esta indicarah se a posicao foi aberta agora (primeiraOrdem)
//Print(__FUNCTION__, ":-| Processandi ORDEM:",tran.order, " DEAL:", tran.deal, " POS ",tran.position, " deal_type:", EnumToString(tran.deal_type) );
closer = true;
OsOrdem* ord;
if( m_ordens.TryGetValue(tran.order, ord) ){
//Print(__FUNCTION__, ":-| ORDEM:",tran.order, " encontrada!" );
//ord.idOrd = tran.order ; // ok
ord.idDea = tran.deal ; // ok
ord.idPos = tran.position ; // ok
ord.typTra = tran.type ; // ok
//ord.typOrd = tran.order_type ; // ok
ord.typDea = tran.deal_type ; // ok
//ord.status = tran.order_state; // ok
//ord.symbol = tran.symbol ; // ok
ord.price = tran.price ; // ok
ord.vol = tran.volume ; // ok
}else{
//deal de uma ordem nao registrada, incluimos agora...
//Print(__FUNCTION__, ":-| ORDEM:",tran.order, " NAO encontrada! Criando uma agora..." );
ord = new OsOrdem;
ord.idOrd = tran.order ; // ok
ord.idDea = tran.deal ; // ok
ord.idPos = tran.position ; // ok
ord.typTra = tran.type ; // ok
//ord.typOrd = tran.order_type ; // ok
ord.typDea = tran.deal_type ; // ok
// ord.status = tran.order_state; // ok
ord.symbol = tran.symbol ; // ok
ord.price = tran.price ; // ok
ord.vol = tran.volume ; // ok
m_ordens.Add(ord.idOrd,ord);
}
// atualisando resumo da posicao e definindo se a ordem eh IN ou OUT...
if( m_idPos==0 || m_idPos!=ord.idPos ){ // ordem de abertura da posicao
//Print(__FUNCTION__, ":-| ORDEM:",tran.order," POSICAO estava zerada. Incluindo ordem na posicao:",ord.idPos );
Print(__FUNCTION__, ":-| NOVA POS:",ord.idPos," idDeal:",ord.idDea," ord:",tran.order," tranDealTyp:",EnumToString(tran.deal_type)," posAnt:",m_idPos );
m_idPos = ord.idPos ; // id da posicao atual
m_type = ord.typDea; // o mesmo tipo do primeiro trade da posicao (compra ou venda).
m_volIn = ord.vol ; // volume de trades IN.
ord.inOut = 1;
//---------- trade deve ser fechado
toClose = true ; closer=false;
toCloseVol = ord.vol ;
toCloseidDeal = ord.idDea ;
toCloseTypeDeal = ord.typDea;
toClosePriceIn = ord.price ;
toCloseOpenPos = true ; // esta eh a transacao de abertura da posicao
//----------
}else{
//<TODO> colocar o codigo para tratar a chegada de um deal de outra posicao
//m_idPos = tran.position ; // id da posicao atual
//Print(__FUNCTION__, ":-| ORDEM:",tran.order," Checando se eh a mesma direcao da posicao:", EnumToString(m_type) );
// transacao com o mesmo tipo da posicao, eh IN, senao eh OUT.
if( m_type == ord.typDea ){ //&& m_volIn>m_volOut
//Print(__FUNCTION__, ":-| direcao da posicao e da transacao sao iguais..." );
m_volIn += ord.vol;
ord.inOut = 1;
//---------- trade deve ser fechado
toClose = true; closer=false;
toCloseVol = ord.vol;
toCloseidDeal = ord.idDea;
toCloseTypeDeal = ord.typDea;
toClosePriceIn = ord.price ;
//----------
}else{
//Print(__FUNCTION__, ":-| direcao da posicao e da transacao sao diferentes...");
m_volOut += ord.vol;
ord.inOut = -1;
}
toCloseOpenPos = false; // esta NAO eh a transacao de abertura da posicao
//Print(__FUNCTION__, ":-| MESM POS:",ord.idPos," idDeal:",ord.idDea," ord:",tran.order," tranDealTyp:",EnumToString(tran.deal_type));
}
//StringConcatenate( m_logPosicao,m_logPosicao,"\n:",tran.position,":",ord.idDea,":",ord.idOrd,":",ord.inOut,":",EnumToString(ord.typDea),":",ord.vol,":",ord.price,":",getMicrosec(),":::"); // string com os eventos ao longo da vida da posicao.
//Print(__FUNCTION__, ":-| gravando no log da posicao...", m_logPosicao);
// verificando se chegou o final da posicao...
if( m_volIn <= m_volOut ){
StringConcatenate( m_logPosicao,m_logPosicao,"FINAL:"); // string com os eventos ao longo da vida da posicao.
Print(m_logPosicao);
//Print(__FUNCTION__, ":-| gravando FINAL no log da posicao...");
initialize();
}
return;
}
//----------------------------------------------------------------------------
// Busca e retorna o comentario de uma ordem.
// Se encontrar a ordem, retorna true grava o comentario na variavel comment
// Se nao encontrar a ordem, retorna false.
//----------------------------------------------------------------------------
bool osc_position::getComment( const ulong ticket, string& comment ){
OsOrdem* ord;
if( m_ordens.TryGetValue(ticket, ord) ){
comment = ord.reqComent;
return true;
}
return false;
}
//----------------------------------------------------------------------------
//+-----------------------------------------------------------+
//| |
//+-----------------------------------------------------------+
void osc_position::onTradeTransaction( const MqlTradeTransaction& tran , // transacao
const MqlTradeRequest& req , // request
const MqlTradeResult& res , // result
bool& closer , // true: trade eh um fechamento de posicao
bool& toClose , // true: trade deve ser fechado
ulong& toCloseidDeal , // se toClose=true este serah o ticket do trade a ser fechado
double& toCloseVol , // se toClose=true este serah o volume do trade a ser fechado
ENUM_DEAL_TYPE& toCloseTypeDeal, // se toClose=true este serah o sentido do trade a ser fechado, conforme ENUM_DEAL_TYPE
double& toClosePriceIn , // se toClose=true este serah o preco do trade a ser fechado
bool& toCloseOpenPos ){ // se toClose=true esta indicarah se a posicao foi aberta agora (primeiraOrdem)
toClose = false;
switch(tran.type){
case TRADE_TRANSACTION_ORDER_ADD: // adicao de nova ordem aberta...
//processarOrderAdd (tran,req,res);
break;
case TRADE_TRANSACTION_ORDER_UPDATE: // modificacao de uma ordem em aberto
//processarOrderUpdate (tran,req,res);
break;
case TRADE_TRANSACTION_REQUEST: // foi feito um pedido
processarTransactionRequest(tran,req,res); break;
case TRADE_TRANSACTION_DEAL_ADD: // adicao de novo negocio para o historico...
processarDealAdd(tran,req,res,closer,toClose,toCloseidDeal,toCloseVol,toCloseTypeDeal, toClosePriceIn, toCloseOpenPos); break;
case TRADE_TRANSACTION_ORDER_DELETE: // remocao de uma ordem da lista de ordens em aberto
processarOrderDelete(tran);
//Print(__FUNCTION__, ":", strTransaction (tran,req,res) );
break;
case TRADE_TRANSACTION_HISTORY_ADD: // adicao de uma nova ordem para o historico...
//Print(__FUNCTION__, ":", strTransaction (tran,req,res) );
break;
//default:
// Print(__FUNCTION__, ":", strTransaction(tran,req,res) );
}
//Print( m_ordens.toString() );
}