Ccxt: Depósito unificado e histórico de transações de retirada

Criado em 14 fev. 2018  ·  64Comentários  ·  Fonte: ccxt/ccxt

O depósito unificado e o histórico de retirada foram implementados em qualquer
das trocas ainda (ou classes básicas para esse assunto)? Eu esperava encontrar um para usar como exemplo.

enhancement

Comentários muito úteis

@kroitor você é um exemplo fantástico de mantenedor de projeto 👌, obrigado pelo trabalho árduo (desculpe que esta mensagem irá spam para todos no tópico - mas eu queria ter certeza de dizer _Obrigado_).

Todos 64 comentários

O depósito unificado e o histórico de retirada foram implementados em qualquer
das trocas ainda (ou classes básicas para esse assunto)?

Ainda não, esta é a parte final da API unificada, vamos abordar esse aspecto imediatamente após cobrir as taxas de financiamento (e unificar todas as taxas em geral, este trabalho está em andamento agora, como você pode ver nas solicitações pull # 1669, # 1814, # 1835).

Suas contribuições são muito bem-vindas, como sempre)

Eu proporia começar com uma ou duas implementações experimentais para uma ou duas bolsas principais para criar estruturas unificadas para depósitos e retiradas. Vou carregá-los em breve, para que todos os desenvolvedores possam continuar a partir daí.

@kroitor Existe uma proposta sobre esta implementação?

@ZohaibAhmed ainda não, mas achamos que deve ser muito semelhante à API de trades unificados ...

Aqui está:

// returns deposits and withdrawals
fetchTransactions (currency = undefined, since = undefined, limit = undefined, params = {})

// returns deposit transactions
fetchDeposits (currency = undefined, since = undefined, limit = undefined, params = {})

// returns withdrawal transactions
fetchWithdrawals (currency = undefined, since = undefined, limit = undefined, params = {})

Uma transação seria semelhante a uma negociação:

    {
        'info':       { ... },                  // the original decoded JSON as is
        'id':        '12345-67890:09876/54321', // string transaction id
        'txid':      'ddsjfdlskjflksdjfkldsjf', // txid in terms of corresponding currency
        'timestamp':  1502962946216,            // Unix timestamp in milliseconds
        'datetime':  '2017-08-17 12:42:48.000', // ISO8601 datetime with milliseconds
        'currency':  'ETH',                     // currency code 
        'status':    'pending',                 // status of the transaction, "pending", "ok"... to be discussed
        'side':      'deposit',                 // direction of the transaction, 'deposit' or 'withdraw'
        'price':      0.06917684,               // float price in quote currency
        'amount':     1.5,                      // absolute amount of base currency
        'fee': {
            'cost': ..., // we also need to somehow designate if...
            'rate': ..., // ...it is a network fee or the exchange fee or both
        },
        ... ? ...
    },

Do exposto, segue-se que ainda há questões a serem discutidas. Suas propostas e sugestões são bem-vindas.

@kroitor parece bom, exceto as taxas. Nem todas as trocas (por exemplo, Bittrex) incluem taxas em sua API de retirada / depósito. Devemos omitir isso por enquanto?

Eu preciso implementar depósitos e retiradas para Binance.

@ZohaibAhmed você implementou isso?
@kroitor você tem uma especificação ou apenas o que está neste tópico?

Quero começar com isso, mas não quero duplicar nenhum trabalho que já está sendo feito. Também quero ter certeza de que corresponde à API padrão.

Posso modelar a API de comércio unificado como um ponto de partida.

@grandmore , ainda não comecei a implementar isso.

Um problema ao trabalhar com dados de retirada / depósito é que o valor pode ser positivo ou negativo para retiradas. A API de negociação atual não mostra valores negativos, ela os converte em valores positivos. Vamos nos certificar de que podemos adicionar isso às especificações?

@ZohaibAhmed É um ponto interessante. Podemos rastreá-los no estilo +/- (crédito / débito) ou sinalizá-los como depósito / retirada.

Vou empurrar algum código para uma bifurcação em algumas horas. Eu criei uma versão Binance, enquanto tento seguir as especificações. Muitas das trocas não têm a sutileza necessária, portanto, muito do trabalho precisará ser feito na biblioteca.

@grandmore

você tem uma especificação ou apenas o que está neste tópico?

O que está neste tópico ...

Vou empurrar algum código para uma bifurcação em algumas horas.

Apenas certifique-se de ler com atenção: https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how -to-contrib-code

Valeu!

@kroitor Sim, o que há neste tópico, como é tudo o que existe. Além de modelar as outras funções da biblioteca.
É claro que li toda a documentação, bem como os documentos CONTRIBUTING.md e fiz experiências com a biblioteca nos últimos dias.

Existem alguns conceitos inteligentes na biblioteca. De qualquer forma, vou empurrar para uma bifurcação para não impactar a biblioteca diretamente. Se for útil, você pode mesclá-lo.

Vou empurrá-lo para uma bifurcação amanhã, pois estou atrasado.

@kroitor Eu empurrei o primeiro rascunho do Binance para esta bifurcação: transações para retirada e depósito

Eu apenas criei o saque e os depósitos seguindo esta especificação

// returns deposit transactions
fetchDeposits (currency = undefined, since = undefined, limit = undefined, params = {})

// returns withdrawal transactions
fetchWithdrawals (currency = undefined, since = undefined, limit = undefined, params = {})

As fetchTransactions unificadas restantes farei depois de esclarecer algumas questões.

// returns deposits and withdrawals
fetchTransactions (currency = undefined, since = undefined, limit = undefined, params = {})

Há muito pouca compatibilidade nas bolsas que vejo.
Binance api
Poloniex Api
Coinbase Api
GDAX Api

A API Kraken também possui /private/Ledgers com histórico de conta.

Os depósitos e retiradas fetchMyTrades no Bitstamp está incorreta. Depósitos e retiradas são indicados pelo campo type na entrada. Não tenho certeza se eles fornecem txid para transações criptográficas por meio de sua API, embora essas informações estejam disponíveis por meio da interface da conta conectada.

@kroitor Estou escrevendo código para fetchDeposits & fetchWithdrawals porque precisamos deles agora. Eu seguiria a especificação acima. Posso escrever este código e fazer solicitações de pull para algumas das trocas? Você acha que poderia mesclar essas alterações?

@lfern, suas contribuições são bem-vindas)

@lfern , estou na mesma posição. Preciso implementar agora, por isso comecei no fim de semana.

Eu tenho um protótipo funcionando e agora estou comparando todas as trocas para encontrar a estrutura de dados comum. Depois de terminar, iria propor a forma dos dados que podem ser comuns e algumas abordagens para algumas das variações.

Um dos principais problemas que vejo é que algumas das bolsas não têm um número de identificação para identificar exclusivamente a transação. Por isso, tenho pensado em criar um hash dos dados que são comuns, para criar uma chave que sempre corresponda à transação.

Eu ia fazer isso amanhã, então terei uma funcionalidade completa de retirada / depósito para Binance. Supondo que seja aceito, eu iria avançar e fazer a primeira meia dúzia de trocas.

@kroitor o que você precisa para colocar este código no repositório principal? É estranho para mim escrever vários branches sem usá-los juntos, o que significa que preciso mesclar os branches no meu fork. Existe uma maneira melhor?

@kroitor o que você precisa para colocar este código no repositório principal?

@grandmore basicamente, basta fazer uma solicitação de pull e a partir daí devemos ser capazes de resolver isso rapidamente ... precisamos apenas de um arquivo js da troca) thx)

@kroitor Deixe-me terminar o código e farei isso amanhã. Na verdade, são 2 arquivos, pois o Exchange.js precisa conter as parseTransactions () comuns

parseTransactions (transactions, side = undefined, market = undefined, since = undefined, limit = undefined) {
  let result = Object.values (transactions || []).map (transaction => this.parseTransaction (transaction, side))
  result = sortBy (result, 'timestamp')
  let symbol = (typeof market !== 'undefined') ? market['symbol'] : undefined
  return this.filterBySymbolSinceLimit (result, symbol, since, limit)
} 

@kroitor @grandmore Estou escrevendo o código cobinhood para transações. O que você acha dos valores possíveis para o status retornado para transações?

parseTransactionStatus (status) {
        if (status in ['tx_pending_two_factor_auth', 'tx_pending_email_auth', 'tx_pending_approval']) {
            return 'pending_user_action';
        } else if (status === 'tx_approved') {
            return 'approved';
        } else if (status in ['tx_processing', 'tx_pending', 'tx_sent']) {
            return 'pending';
        } else if (status in ['tx_timeout', 'tx_invalid', 'tx_cancelled', 'tx_rejected']) {
            return 'canceled';
        } else if (status in ['tx_confirmed']) {
            return 'ok';
        } else {
            'unkown';
        }
    }

@ifern Cada troca tem respostas diferentes, então podemos padronizá-las da resposta de deixar a troca ou encontrar um meio-termo. Gosto da sua abordagem de agrupamento, essencialmente os mesmos erros em um tipo de resposta ['tx_timeout', 'tx_invalid', 'tx_cancelled', 'tx_rejected'] = cancelled.

Isso é o que fiz pela Binance.

parseTransactionStatus (status, side) {
  if (!(side === 'deposit' || side === 'withdraw'))
    throw new ExchangeError (this.id + 'deposit/withdraw side set incorrectly: ' + side);
  if (side === 'deposit') {
    let statuses = {
        '0': 'pending',
        '1': 'complete',
    };
    return (status in statuses) ? statuses[status] : status.toLowerCase ();
  } else {
    let statuses = {
        '0': 'email sent',
        '1': 'cancelled',
        '2': 'awaiting approval',
        '3': 'rejected',
        '4': 'processing',
        '5': 'failure',
        '6': 'complete',
    };
    return (status in statuses) ? statuses[status] : status.toLowerCase ();
  }
}

Talvez seja bom diferenciar 'rejeitado' e 'falha' de 'cancelado'. Além disso, gostaria de diferenciar 'pendente' quando a troca está processando a solicitação e 'pendente' quando a troca está aguardando etapas adicionais do usuário. Mas não sei se esse estado adicional seria realmente útil para outros.

type TransactionStates = (
  'ok' |
  'cancelled' |
  'pending' |
  'approved' |
  'rejected' |
  'failure' |
  'pending-user-action');

O que você acha dos valores possíveis para o status retornado para transações?

Eu iria com a menor variedade de status possível, a saber:

  • ok (o depósito creditado ou financiado / recebido ou retirada foi enviada, foi aprovada para envio, etc ...)
  • pending (depósito pendente / confirmando ou retirada aguardando confirmação ou aprovação)
  • error (seja qual for o motivo, rejeitado por qualquer parte ou falhou)
  • canceled (a maioria dos usuários provavelmente irá querer, no entanto, muitas trocas não reportarão transações históricas canceladas)

Para mim, os códigos acima parecem alinhados com o que obteríamos das APIs das bolsas em média ... O resto dos status - não tenho tanta certeza ... Ainda aberto a discussão e argumentos.

@kroitor Eu criei um id sintético para que as transações possam ser identificadas mesmo quando a troca não retornar um id. Preciso ser capaz de identificar transações e essa parece ser a melhor maneira de consegui-lo.

const syntheticId = this.hash (timestamp.toString () + currency.id + side + amount + address);

O id é calculado para que sempre retorne o mesmo id quando recomputado. Mas tenho 2 perguntas.

  1. O hash id tem que ser idêntico quando criado a partir de qualquer uma das linguagens suportadas. Estou usando o this.hash() padrão. Não codifico em python, então gostaria de verificar com você se isso funciona em python.

  2. Posso retornar o id sintético no lugar do id ou retornar um id anulado e adicionar um hash id, por exemplo. O que voce prefere?

Esse

let result = {
  ...
  'id': null, 
  'hashid': syntheticId,
  ...
};

ou isto

let result = {
  ...
  'id': syntheticId, 
  ...
};

Como organizo uma solicitação pull?

Posso retornar o id sintético no lugar do id ou retornar um id anulado e adicionar um hash id, por exemplo. O que voce prefere?

Eu preferiria que o id fosse undefined se for desconhecido da troca e manter o hash id em seu userland fora da biblioteca, já que ter o hash id é uma tarefa específica do usuário. Nem todos os usuários precisam de chamadas excessivas. Em outras palavras, eu preferiria dados normalizados onde normalizado significa que os dados não devem ser duplicados, e se um campo é sempre reconstruído a partir dos mesmos dados, eu preferiria não desnormalizá-lo (para não adicionar mais campos ou casos especiais).

Como organizo uma solicitação pull?

Em geral: https://help.github.com/articles/creating-a-pull-request/

Você bifurca o repositório para seu github. Em seguida, faça as alterações. Em seguida, há um botão para criar uma solicitação de pull dessas alterações para o repositório original.

Com relação ao ccxt: https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how -to-contrib-code

Deixe-nos saber se você tiver outras perguntas. Valeu!

Para o objeto fees , você viu alguma troca que cobra taxas em uma moeda diferente da moeda da transação? Mesmo se não, seria bom se o objeto fees fosse do mesmo tipo em várias classes, então a moeda seria incluída em fees mesmo que fosse igual ao saque.

@clarkmoody, nossa estrutura de taxas padrão (você pode encontrá-la entre pedidos e negociações) em toda a biblioteca é:

fee: {
    'cost': 123.45,
    'rate': 0.001,
    'currency': 'ETH',
}

)

nossa estrutura de taxas padrão

Excelente! Eu estava pensando, já que a estrutura de dados de exemplo anterior neste tópico não tinha todos esses campos.

@kroitor Eu criei uma solicitação de pull para o código de depósito e retirada Binance. (https://github.com/ccxt/ccxt/pull/2526)

@grandmore thx! Vou mesclá-lo em breve com este: https://github.com/ccxt/ccxt/pull/2355 por @lfern )

@kroitor Alguma notícia sobre este PR sendo adicionado? Obrigado.

@grandmore terrivelmente pelo atraso, irá mesclar isso após o # 2397 no menor tempo possível.

@kroitor Eu gostaria de adicionar as taxas de retirada ao Binance, mas elas são calculadas em vez de retornadas diretamente da API.
eu posso adicionar

const withdrawFee = await this.wapiGetWithdrawFee(this.extend(para, params));

Que retornará withdrawFee: 0.0005

Eu gostaria de adicionar'drawFee 'aos dados globais do objeto para que ele possa ser referido no loop' parseTransaction () '. Você tem essa estrutura no topo, mas ela diz que está desatualizada. Onde posso armazenar isso?

'fees': {
    'trading': {
    'tierBased': false,
    'percentage': true,
    'taker': 0.001,
    'maker': 0.001,
},
// should be deleted, these are outdated and inaccurate
'funding': {...

Posso apenas adicionar assim?

'fees': {
    'trading': {
    'tierBased': false,
    'percentage': true,
    'taker': 0.001,
    'maker': 0.001,
    'withdrawFee': 0.0005
},
// should be deleted, these are outdated and inaccurate
'funding': {...
...

Eu só preciso verificar antes de escrever qualquer coisa.

@kroitor Eu também aguardo ansiosamente a fusão. Existe uma maneira de ser notificado quando isso acontecer?

alguma atualização disso? :)

@kroitor Com relação à sua proposta acima , não acho que price deva estar na estrutura de dados da transação.

Existe uma razão convincente para mantê-lo?

@clarkmoody você está certo, é um artefato de copiar a estrutura de negociações, o preço não pertence a isso.

Sei que estou um pouco atrasado para esta festa, mas proponho usar fetchTransfers vez de "transações" para a API privada unificada, já que _transações_ comunica qualquer movimento (negociação, depósito, retirada, etc). É claro que separar os depósitos e retiradas e fazer as transferências possivelmente compõem esses trabalhos, embora muitas trocas com as quais trabalhei os mantenham combinados em um único ponto de extremidade.

@mrasband thx, também considerarei isso.

@kroitor existe uma atualização sobre isso? Você escreveu "Começará a mesclar isso em breve)" em abril. Estamos em agosto e há vários PRs implementando métodos de saques e depósitos, também aguardando a fusão. Ainda há coisas obscuras / a serem definidas? Ou o que está bloqueando esses PRs?

@firepol , desculpe, isso é totalmente minha culpa, inicialmente queríamos coletar mais informações sobre outras trocas para ainda mais unificação, mas depois me enterrei em outras partes da biblioteca e manutenção e não prestei atenção suficiente a esta seção específica do a API unificada. Eu sei que foi um longo tempo de espera e isso não é realmente aceitável. Minhas desculpas a todos os que se comprometeram e a todos os usuários da biblioteca que antecipam esse recurso, farei o possível para mesclar todos os PRs relacionados antes do final da próxima semana.

@kroitor você é um exemplo fantástico de mantenedor de projeto 👌, obrigado pelo trabalho árduo (desculpe que esta mensagem irá spam para todos no tópico - mas eu queria ter certeza de dizer _Obrigado_).

Tudo bem, eu combinei gdax, gemini e cobinhood. Binance está a caminho. Adicionados alguns esclarecimentos ao Manual, mas ele ainda está incompleto. Sua ajuda para adicionar o restante dos métodos e documentos será muito apreciada!

Notas principais:

  • campo side renomeado para campo type
  • valores possíveis para o campo type : 'deposit' ou 'withdrawal' (não withdraw , mas withdrawal !)
  • adicionado o campo address à estrutura da transação (designa o remetente ou o destinatário da transação, dependendo do tipo)
  • os valores possíveis para um status são: 'ok' (confirmado e creditado para saldo), 'pending' (não confirmado ou não creditado ainda), 'failed' , 'canceled'

Eu encerraria este problema por enquanto, mas fique à vontade para continuar postando aqui ou em outro lugar se tiver qualquer problema ou dificuldade com o acima. Prosseguirei com as implementações de polimento e adicionando correções de bugs quando necessário. Obrigado!

Olá @kroitor ,

campo lateral renomeado para campo de tipo

Em python, type é o nome de um embutido, então acho uma boa ideia não usar esse nome que substitui o embutido e se restringe a side . Isso não deve ser um problema se você usá-lo dentro de um json, pois parece ser o uso atual, mas pode ser confuso porque todos os métodos usarão "lado" e no json não é lado, mas tipo. Além disso, as pessoas estão acostumadas com a terminologia paralela para identificar compra / venda, então por que não manter a mesma terminologia também para depósito / retirada? Talvez você quisesse separar os 2 conceitos de compra / venda e depósito / retirada, usando um campo diferente ... mas acho que não é realmente necessário ... Pensamentos?

então por que não manter a mesma terminologia também para depósito / retirada?

porque, você mesmo disse:

as pessoas estão acostumadas com a terminologia paralela para identificar compra / venda,

) Em outras palavras, se side é buy ou sell todos os lugares, não queremos realmente adicionar um terceiro e um quarto lado possível à lista de valores possíveis para esse campo. Isso aumentará a confusão, eu acho. Portanto, type .

PS Obviamente, o próprio campo type também é usado com pedidos ( limit , market ), portanto, também não é o melhor nome para a direção de financiamento. Também estávamos pensando em chamá-lo de direction , no entanto, acabamos com type por enquanto, pois achamos que seria semanticamente próximo ao que as próprias trocas respondem.

@kroitor question para aquelas trocas que não têm métodos diferentes para buscar saques e buscar depósitos, como:

  • bitstamp (pode obter todo o histórico, incluindo negociações)
  • bitfinex (depósitos + retiradas)
  • ...

É fetchTransactions ser definido assim?

  • 'fetchTransactions': true,
  • 'fetchWithdrawals': false,
  • 'fetchDeposits': falso,

Vejo que fetchTransactions está implementado para essas trocas:

  • exmo
  • gdax
  • Gêmeos
  • bitstamp foi implementado incorretamente como fetchMyTrades e o problema # 3749 já está resolvendo este problema

Proponho adicionar um novo parâmetro no método, para o type .

  • undefined : todos os tipos de transações (depósitos, retiradas, negociações)
  • withdrawal : devolver apenas retiradas
  • deposit : devolver apenas depósitos

Por exemplo, no bitfinex, você chamaria esse método sem especificar o tipo e obteria todos os depósitos e retiradas. Ou ligue 2 vezes. Primeiro com type = 'deposit' , depois com type = 'withdrawal' .

No bitstamp, você chamaria esse método sem especificar o tipo e obteria todos os depósitos e retiradas (como no bitfinex). Como não se espera que as negociações sejam retornadas nesta resposta do método, elas devem ser filtradas.
Ou chame-o 2 vezes, como para bitfinex: primeiro com type = 'deposit' , depois com type = 'withdrawal' .
Para obter mytrades, o método fetchMyTrades deve ser chamado, e fetchMyTrades deve filtrar as transações (retirada e depósito), não esperadas nessa chamada de método.

O que você acha?

Ficaria feliz em contribuir nessa área, pois estou escrevendo um software de contabilidade que precisa buscar históricos de trocas.

Posso começar com bitstamp, pois deve ser fácil determinar o tipo de transação sem complicações (consulte a discussão em PR # 3736)

É fetchTransactions definido assim?

  • 'fetchTransactions': true,
  • 'fetchWithdrawals': false,
  • 'fetchDeposits': falso,

Sim )

Proponho adicionar um novo parâmetro no método, para o tipo.

Eu realmente não acho que devemos fazer isso. Sugiro que continuemos filtrando fora da biblioteca (no espaço do usuário). Para explicar, pense da seguinte maneira: se não houver fetchDeposits e fetchWithdrawals nativos e apenas fetchTransactions estiver disponível, significa que a troca em questão produzirá todos eles misturados , sem o filtro. Caso contrário, se a troca permitisse filtrá-los, fetchDeposits nativo ou fetchWithdrawals seria verdadeiro, e então o usuário apenas chamaria o método correspondente em primeiro lugar.

Portanto, nos casos em que temos apenas fetchTransactions CCXT fará primeiro o download de toda a resposta (contendo transações de tipos diferentes). Em seguida, a biblioteca deve analisar cada uma das transações baixadas para descobrir seus tipos reais unificados. Observe que analisaremos todos os depósitos e retiradas de qualquer maneira. Neste ponto, todas as transferências foram analisadas, então, tendo feito a parte mais difícil do trabalho, por que uma biblioteca as filtraria? Parece uma limitação artificial (desnecessária) de resultados, embora nós realmente tratássemos de todos eles. Preferimos retornar todos os resultados analisados ​​e deixar o usuário decidir. Além disso, oferecemos um conjunto útil de métodos, e a maioria das linguagens oferece sintaxe de açúcar para filtros e mapas, assim:

// JavaScript

// native js syntax filters
transactions = await exchange.fetchTransactions ()
deposits = transactions.filter (t => t.type === 'deposit')
withdrawals = transactions.filter (t => t.type === 'withdrawal')

// alternatively, filter with ccxt
grouped = exchange.groupBy (transactions, 'type')
console.log (grouped['deposit'])
console.log (grouped['withdrawal'])
# Python

# native python syntax filters
transactions = exchange.fetch_transactions ()
deposits = [t for t in transactions if t['type'] == 'deposit']
withdrawals = [t for t in transactions if t['type'] == 'withdrawal']

# alternatively, filter with ccxt
grouped = exchange.group_by(transactions, 'type')
print(grouped['deposit'])
print(grouped['withdrawal'])
// PHP

// native php syntax filters
$transactions = $exchange->fetch_transactions ();
$deposits = array_filter ($transactions, function ($t) { return ($t['type'] === 'deposit'; });
$withdrawals = array_filter ($transactions, function ($t) { return ($t['type'] === 'withdrawal'; });

// alternatively, filter with ccxt
$grouped = $exchange->group_by($transactions, 'type');
echo print_r($grouped['deposit'], true) . "\n";
echo print_r($grouped['withdrawal'], true) . "\n";

Então, como você pode ver acima, não há muito sentido em ter o filtro dentro da biblioteca - imagine que cada biblioteca duplicaria todos os filtros em seu código-fonte reinventando a roda continuamente ... Isso não é o que realmente deseja em termos de design de biblioteca. A ideia por trás disso é: se for facilmente realizável com a sintaxe da linguagem nativa de uma forma muito curta ou com uma cláusula de uma linha - então ele não pertence à base de código da biblioteca . Esperançosamente, isso explica por que não adicionaríamos um filtro para fetchTransactions . Teríamos apenas fetchTransactions sem um tipo e é isso.

Por outro lado, você poderia embrulhar fetchTransactions com has['fetchDeposits'] = 'emulated' , conforme descrito no Manual de trocas has properties: https://github.com/ccxt/ccxt/wiki / Manual # troca -propriedades e com um básico adicional
invólucro que chamaria fetchDeposits → fetchTransactions+filter e fetchWithdrawals → fetchTransactions+filter .

Assim, em vez de chamar fetchTransactions com um type especificado, você prefere chamar versões emuladas de fetchDeposits ou fetchWithdrawals wrappers. No entanto, por causa da paginação, isso pode realmente se tornar complicado (homenagear limit e since com resultados filtrados). Assim, no final, não nos incomodaríamos em emular nada que não seja muito bem suportado pela troca em questão (não vale o esforço, pensamos).

Ficaria feliz em contribuir nessa área, pois estou escrevendo um software de contabilidade que precisa buscar históricos de trocas.
Posso começar com bitstamp, pois deve ser fácil determinar o tipo de transação sem complicações (consulte a discussão em PR # 3736)

Sua ajuda para resolver o problema de bitstamp ainda é muito bem-vinda! Na verdade, qualquer ajuda ainda é bem-vinda, então se o acima exposto não fizer sentido para você e você quiser tentar unificar esse aspecto de uma forma mais elegante - ficaremos felizes em revisá-lo e mesclá-lo. Avise-me se tiver mais perguntas. Muito obrigado!

Obrigado @kroitor pela sua boa resposta e referências ao manual, uau, esta biblioteca é realmente complexa, bem documentada e sempre há algo novo para aprender.

Faz todo o sentido a sua explicação sobre não filtrar coisas e deixar que isso fique no espaço do usuário.

O caso do bitstamp é um pouco complicado então. Porque no # 3749 o usuário não esperava que os depósitos e retiradas aparecessem em fetchMyTrades e eu concordo com ele.

Como um usuário do CCXT, tentaria usar os métodos sem verificar o código e cada documentação de troca. Espero que a biblioteca seja fácil de usar e forneça o máximo de informações possível, portanto, não preciso ler a documentação oficial da API do Exchange.

Dito isso, quando um método não retorna o que o nome do método diz, o método deve ser documentado para que o usuário saiba o que ele não retorna, quando ele passar ou clicar nele em seu IDE.

Vejo 2 soluções para bitstamp.

1) Implemente fetchTransactions e basicamente é o mesmo que fetchMyTrades.
2) Livre-se de fetchMyTrades e tenha apenas fetchTransactions.

Eu sugeriria a solução nº 2 porque o ponto de extremidade de bitstamp a ser usado é chamado transactions portanto, se um usuário procurar on-line, ele saberá que deve pesquisar algo chamado "transações" e não "fetchmytrades".

Se houver muitas trocas que funcionam como bitstamp e têm apenas um ponto de extremidade para buscar todas as transações, talvez um novo método "unificado" possa ser criado chamado fetchAllTransactions que justifique fetchMyTrades: false, por exemplo, para bitstamp seria:

'fetchMyTrades': false,
'fetchTransactions': false,
'fetchWithdrawals': false,
'fetchDeposits': false,
'fetchAllTransactions': true,

Como se este fosse ver tudo falso, mas fetchAllTransactions é verdadeiro, isso significa que é um ponto de extremidade como o bitstamp, obtendo tudo de uma vez.

Assim, ficaria claro também para o usuário que preencheu o número 3749 que ele deveria usar um dos grouped ou métodos de filtragem na linguagem de programação nativa e filtrar o que ele precisa da enorme lista que contém tudo.

O que você acha?

Atualização : a desvantagem da solução 2 é que ela quebrará as coisas para todas as pessoas que dependem de fetchMyTrades. Mas você pode dizer a eles que eles devem sempre verificar se um recurso está disponível, se não verificar o próximo etc. como se eu tivesse que automatizar a verificação de um monte de histórico de depósito de trocas, eu implementaria em meu userland uma função auxiliar para verificar exatamente isso.

O que você acha?

Eu sugeriria um meio-termo - poderíamos ir para a solução1, mantendo fetchMyTrades e fetchTransactions (ambos mapeados para o mesmo método comum, basicamente), e retornaríamos type: undefined/None/null , type:'limit' , type: 'market' , type:'withdrawal' , type:' 'deposit' de fetchMyTrades e fetchTransactions, onde / se possível. E eu adicionaria um aviso de exceção que tornaria este código autodocumentado, algo como:

async fetchMyTrades (...) {
    return await this.fetchMyTradesAndTransactions (type, ...); // type might not be needed
}

async fetchTransactions (...) {
    return await this.fetchMyTradesAndTransactions (type, ...); // type might not be needed
}

async fetchMyTradesAndTransactions (type, ...) {
    if (this.options['fetchMyTradesAndTransactionsWarning']) {
        const warning = this.id + " " +
            "fetchMyTrades / fetchTransactions will return all trades and transactions " +
            "mixed in the same array. Make sure you add proper handling and set this.options['fetchMyTradesAndTransactionsWarning'] = false " +
            "or add { 'options': { 'fetchMyTradesAndTransactionsWarning': false }} to exchange constructor " +
            "to acknowledge this message and turn off this warning.";
        throw new ExchangeError (warning);
    }
    ...
    // parsing overrides to handle them both
    return ...;
}

O usuário simplesmente não poderá ignorá-lo e forçaremos a conscientização sobre o comportamento específico de troca acima dessa forma. Como isso soa para você?

Eu gosto da exceção, é uma maneira bastante inteligente de alertar o usuário sobre a situação incomum sem forçá-lo a verificar o documento oficial de troca e fornecer uma opção para se livrar do aviso.

Então essa seria a troca excepcional, aquela que vai filtrar / agrupar as transações com base no tipo? Ou o que você quer dizer com:

    // parsing overrides to handle them both
    return ...;

? Você quer chamar parseTrades e depois também chamar parseTransactions?

Fiz um rascunho inicial da refatoração do bitstamp.

Não entendi exatamente sua intenção com type mas usei desta forma:

tipo: transações ou negócios.

Quando chamo o método comum de:

fetchMyTrades eu defino type = 'myTrades'
fetchTransactions eu defino type = 'transactions'

então:

...
response = call the API
if (type === 'myTrades') {
    result = filter to get only trades
    return parseOrders(result)
} else {
    result = filter to get only trades
    return parseTransactions(result)
}

Usado assim, poderíamos ter usado também um bool, por exemplo, fetchTransactions true | false.

Assim, a única coisa que ainda preciso fazer é implementar o método parseTransaction e fazer a filtragem adequada da resposta. Por favor, corrija-me se você pretendia outra coisa, provavelmente irei trabalhar nisso depois das 20:30 CET, então não tenha pressa em responder;)

Não entendi exatamente sua intenção com o tipo, mas usei desta forma:

Ok, minha intenção provavelmente não estava clara, vamos esclarecer um pouco mais) De acordo com seus documentos, o endpoint user_transactions retorna todas as linhas do razão, que inclui negociações, transações de financiamento (depósitos e retiradas) e transferências internas.

bitstamp-transactions

De volta à nossa API unificada, temos fetchMyTrades (retorna uma matriz de dicionários) e também fetchTransactions (também retorna uma matriz de dicionários, e o método pode estar ausente ou representado pelo dois outros métodos separados - fetchDeposits e fetchWithdrawals , se a troca não permitir buscá-los em uma chamada).

Cada estrutura de negociação unificada tem um type ( limit ou market ):
https://github.com/ccxt/ccxt/wiki/Manual#trade -structure

Cada estrutura de transação unificada também tem um type ( deposit ou withdraw ):
https://github.com/ccxt/ccxt/wiki/Manual#transaction -structure

Porque fetchMyTrades e fetchTransactions retornam arrays de dicionários, e porque todos os dicionários têm type o usuário pode usar esse type para fazer a filtragem e distinguir negociações de transações (sejam depósitos ou retiradas).

Pensando um pouco mais sobre isso, acho que devemos fazer o seguinte:

Por padrão, fetchMyTrades retornaria apenas as transações (linhas que não são transações ou transferências internas, analisadas com parseTrade ), e fetchTransactions retornaria apenas as transações (linhas que não são negociações ou transferências internas, analisadas com parseTransaction ). Isso é consistente com o design de nossa API unificada. E isso é o que um usuário esperaria, a partir da descrição no Manual.

Eu também manteria um aviso amigável dentro de cada um dos dois métodos para garantir que o usuário esteja ciente dos possíveis problemas de paginação. O usuário que deseja todas as transações, apenas chamaria o método implícito diretamente. Para mim, isso parece uma abordagem limpa para o Bitstamp por enquanto. E você?

Também poderíamos adicionar alguns sinalizadores a fetchMyTrades / fetchTransactions para retornar resultados diferenciados, com linhas de tipos mistos analisadas por este ou aquele analisador, mas eu não me incomodaria em emular esse aspecto, na verdade (tentando preencher as lacunas nas trocas 'apis com métodos emulados muitas vezes acaba sendo uma má decisão em termos de arquitetura e experiência do usuário).

Parece bom, estou implementando exatamente assim.

A propósito, tentei usar o método filter , como você sugeriu, mas não transpila corretamente em python.

        if (type === 'myTrades') {
            let result = response.filter (t => t['type'] === '2');
            return this.parseTrades (result, market, since, limit);
        } else {
            let result = response.filter (t => t['type'] === '0' || t['type'] === '1');
            return this.parseTransactions (result, market, since, limit);
        }

Não transpila ... Ainda recebo response.filter (com a função de seta) em python ... o que não funciona, é claro. Soluções alternativas para isso? Ou isso é um bug do transpiler?

Soluções alternativas para isso?

let result = this.filterBy (result, 'type', '2'); // this will transpile properly

Para recapitular, não podemos realmente usar .filter () dentro da lib , porque o transpiler ainda é bastante burro e não sabe como fazer a introspecção de código que tem que fazer para reconhecer fechamentos e mapeamentos e convertê-los para compreensões de lista python adequadas (isso envolve expressões complexas com variáveis ​​de fora do escopo da expressão, o que torna difícil de transpilar). Portanto, temos um conjunto de ajudantes: .filterBy , .groupBy , .indexBy , .sortBy e assim por diante. Se você pesquisar a palavra "filter" em arquivos de origem, você encontrará uma tonelada de exemplos de como ela é usada em outras trocas para filtrar matrizes de resultados por valor-chave. Espero que isso ajude) Deixe-me saber se não) Thx! )

Encontro muitos exemplos de filterBy, mas não exemplos para filtrar por mais de um valor. Em nosso caso, filterBy 2 ok, para obter myTrades.

Mas para obter depósitos (0) E retiradas (1) preciso filtrar por 0 e 1. Temos um método assim? Se não, talvez você possa criar um? Caso contrário, posso tentar fazer um sozinho, deve ser um loop simples.

ATUALIZAÇÃO: ou talvez não para complicar a vida: eu filtro primeiro por 0, depois por 1 e, em seguida, combino os 2 arrays em um. Temos um método auxiliar que transpila corretamente, para combinar 2 matrizes?

Temos um método auxiliar que transpila corretamente, para combinar 2 matrizes?

// ...
let groups = this.groupBy (results, 'type');
let transactions = this.arrayConcat (groups['0'], groups['1']); // 0 (deposits) + 1 (withdrawals)
transactions = this.sortBy (transactions, 'timestamp');
let trades = this.sortBy (groups['2'], 'timestamp');
// ...

Os nomes de campo e valores-chave reais dependem se você está operando com resultados da troca ainda não analisados ​​ou se está operando com resultados já analisados ​​/ unificados.

@firepol desculpe, esqueci que fiz o método .filterByArray antes, então sim, você pode fazer isso em apenas uma linha)

screen shot 2018-09-08 at 00 33 34

Err ... na verdade, o exemplo acima é um péssimo exemplo de como não usar o filterByArray, já que faz a travessia multipass do array, enquanto, naquele caso particular com o bibox mostrado na captura de tela da minha mensagem anterior, tudo o que pode ser feito em apenas uma passagem:

    parseTickers (rawTickers, symbols = undefined) {
        let tickers = [];
        for (let i = 0; i < rawTickers.length; i++) {
            tickers.push (this.parseTicker (rawTickers[i]));
        }
        return this.filterByArray (tickers, 'symbol', symbols);
    }

    parseTickers (rawTickers, symbols = undefined) {
        let tickers = [];
        for (let i = 0; i < rawTickers.length; i++) {
            let ticker = this.parseTicker (rawTickers[i]);
            if ((typeof symbols !== 'undefined') || (this.inArray (ticker['symbol'], symbols))) {
                tickers.push (ticker);
            }
        }
        return tickers;
    }

Mas de qualquer forma, você tem um conjunto completo de ferramentas e pode combiná-las como quiser.

[RANT] Eu te disse que meio que odeio a API bitstamp? Até agora são as piores que eu vi e usei depois de lykke e cex, nr 3 nas 3 APIs de criptografia mais feias vistas até agora ... [/ RANT]

Estou desistindo por esta noite, eu empurrei minha implementação WIP aqui:

https://github.com/firepol/ccxt/tree/bitstamp-fetch-transactions

Vou registrar um bug relacionado ao bitstamp horrível em um segundo.

ATUALIZAÇÃO: na verdade, acho que posso substituir parseTransactions em vez de preencher um bug. Se você estiver curioso, o problema pode ser reproduzido (em meu branch, acabei de empurrar meu fork, vinculado acima) da seguinte maneira (em python):

bitstamp = ccxt.bitstamp('...')
test1 = bitstamp.fetch_transactions('BTC') # this won't work, not a valid market apparently
test2 = bitstamp.fetch_transactions('BTC/USD')

Em test2, BTC / USD torna-se btcusd, que não é uma moeda válida, portanto, em exchange.parse_transactions ele falha com este erro:

  File "/home/paul/projects-python/ccxt/python/ccxt/base/exchange.py", line 1293, in parse_transactions
    code = currency['code'] if currency else None
KeyError: 'code'

@firepol geralmente usamos algum sabor do seguinte:

let symbol = 'BTC/USD';
let market = this.market (symbol); // market['id'] == 'btcusd'
let base = market['base']; // BTC, also baseId = btc
let quote = market['quote']; // USD, also quoteId = usd

Isso ajuda em situações em que você precisa converter um código de moeda em um símbolo de mercado e vice-versa.

Estou com um probleminha, o erro acontece no método exchange.fetch_transactions. Se eu copiar e colar no bitstamp.js para substituí-lo, recebo outro erro de sintaxe porque o transpilador não transpila corretamente a primeira linha:

  File "/home/paul/projects-python/ccxt/python/ccxt/bitstamp.py", line 456
    result = Object.values(transactions or []).map(transaction => self.parse_transaction(transaction, currency))
                                                                ^
SyntaxError: invalid syntax

Desculpe incomodá-lo com tantas perguntas, mas bitstamp é realmente uma dor, e o transpiler também é bastante duro ... Você pode ajudar?

@firepol com certeza! O problema é que não podemos quebrar as seguintes regras: https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#derived -exchange-classes, este em particular:

desdobrar todos os mapas e compreensões para loops for básicos

Como eu disse antes, não podemos transpilar a sintaxe que envolve encerramentos - tudo isso é sintaxe com js .filter e js .map , portanto, substituímos todos os .filter/.map/.forEach por uma chamada para um método base ou um loop for básico. Veja este exemplo:

No entanto, você realmente não precisa se preocupar, pois irei editá-lo e limpá-lo na mesclagem de qualquer maneira. Deixe-me saber se você tem mais)

Voila, pronto. Posso adicionar uma descoberta interessante que acabei de fazer: consulte os documentos da API: https://www.bitstamp.net/api/

Endpoint TRANSACÇÕES DO USUÁRIO: https://www.bitstamp.net/api/v2/user_transactions/ {currency_pair} /

Você pode pensar que pode obter apenas os depósitos (tipo 0) ou retiradas (tipo 1), mas não pode. Para obtê-los, você deve buscar tudo. Quero dizer, a história de todas as negociações para todas as moedas (todos os pares / mercados). É por isso que implementei como fiz. Tive que lutar por muito tempo para entender tudo. Agora está testado e funcionando.

@firepol depois de pensar um pouco mais sobre isso, decidi voltar à sua proposta inicial, e carreguei uma versão mesclada dela, deve estar ok em 1.17.253 e removi algumas partes para tornar a implementação um pouco mais limpa . Deixe-me saber se você acha que devemos adicionar ou editar algo além disso. Obrigado novamente por sua ajuda, seu envolvimento é muito apreciado!

JFYI, achamos que adicionar tag associado a address para certas moedas e transações - é uma coisa razoável a se fazer, para alinhá-lo com o retorno de createDepositAddress/fetchDepositAddress , portanto, começamos adicionando tag a parseTransaction onde faltava e também atualizamos a descrição de uma estrutura de transação no manual.

Esta página foi útil?
0 / 5 - 0 avaliações