O mercado é o campo de batalha, o comprador e o vendedor estão sempre no jogo, que também é o tema eterno do negócio de negociação.
Na negociação de alta frequência, existem dois tipos principais de estratégias. A estratégia do lado do comprador e a estratégia do lado do vendedor. A estratégia do lado do vendedor é geralmente uma estratégia de criação de mercado, e os dois lados dessas estratégias são adversários. Por exemplo, a estratégia do comprador de arbitragem de alta frequência de suavizar todos os fenômenos irracionais no mercado na velocidade mais rápida, tomando a iniciativa de atacar o preço rapidamente ou comer o preço errado de outros criadores de mercado.
Há também uma maneira de analisar os dados históricos ou as regras de ordens do mercado, enviar as ordens pendentes a um preço razoável com antecedência e enviar as ordens de retirada com a rápida mudança do preço de mercado. Tais estratégias são comuns na criação de mercado passivo, uma vez que as ordens pendentes são executadas, e após um certo lucro ou após atingir a condição de stop-loss, a posição será fechada.
Penny Jump traduzido para o inglês é o significado de aumento de micro-preço. O princípio é rastrear o preço de compra e o preço de venda do mercado. Então, de acordo com o preço do mercado, mais ou menos o aumento de micro-preço do preço de rastreamento, é óbvio que esta é uma estratégia de negociação passiva, pertence ao lado do vendedor estratégia de criação de mercado. Seu modelo de negócios e lógica é conduzir transações bilaterais em ordens de limite listadas em bolsa para fornecer liquidez.
A estratégia de criação de mercado requer uma certa quantidade de estoque na mão e, em seguida, negocia tanto no lado do comprador quanto do vendedor. A principal renda desta estratégia é o retorno da taxa de comissão fornecida pela bolsa, bem como a diferença de preço ganha comprando baixo e vendendo alto.
Sabemos que há muitos investidores de varejo no mercado de negociação, e também há muitos grandes investidores, como:
Se um grande investidor quer comprar 500 lotes de petróleo bruto, não há tantas ordens no preço atual para venda, e o investidor não quer comprá-los no preço mais alto. Se eles insistem em enviar a ordem de compra no preço atual, o custo dos pontos de deslizamento será muito grande. portanto, ele tem que enviar uma ordem pendente no preço desejado. Todos os participantes do mercado verão uma ordem de compra hugh mostrando no determinado preço.
Por causa desta ordem enorme, parece desajeitado no mercado, às vezes chamamos de "ordens de elefante".
Selling Price 400.3, Order volume 50; buying price 400.1, Order volume 10.
De repente, este elefante engorroso salta para o mercado, e o preço da oferta foi enviado ao preço de 400.1.
Selling Price 400.3, Order volume 50; Buying price 400.1, Order volume 510.
Todos os traders sabem que se houver uma grande quantidade de ordens pendentes a um determinado preço, então esse preço formará um forte suporte ou resistência.
Selling Price 400.3, Order volume 50; Buying price 400.2, Order volume 1,
O preço 400.1 torna-se
Mesmo que o preço não suba, na posição de
Primeiro, observe as oportunidades de negociação com uma probabilidade muito baixa do mercado e faça estratégias correspondentes de acordo com a lógica de negociação. Se a lógica for complexa, você precisa usar o conhecimento matemático existente, usar o modelo para descrever a natureza do fenômeno irracional tanto quanto possível e minimizar o sobreajuste. Além disso, ele deve ser verificado por um mecanismo de backtest que possa atender ao princípio
Qual é o significado de suportar o mecanismo de backtest
O
Além disso, alguns leitores podem achar que a estratégia Penny Jump requer oportunidades de negociação de mercado, ou seja, a necessidade do mercado tem pelo menos duas lacunas de preço
A situação de aparição de duas diferenças de preço ocorre com muita frequência, mas os três saltos são os mais seguros, mas os três saltos raramente ocorrem, resultando na frequência de negociação é muito baixa.
Em seguida, observamos a diferença entre o anterior
Depois de limpar a lógica de negociação, podemos usar o código para alcançá-lo. Uma vez que a plataforma FMZ Quant usa C ++ exemplos de estratégia de escrita são muito poucos, aqui usamos C ++ para escrever esta estratégia, que é conveniente para todos aprenderem, e a variedade é commodity futures.fmz.com> Login > Dashboard > biblioteca de estratégia > Nova estratégia > Clique no menu suspenso no canto superior esquerdo > Selecione C ++ para começar a escrever a estratégia, preste atenção aos comentários no código abaixo.
Em seguida, vamos olhar para a classe HFT, que tem cinco métodos. O primeiro método é o método de construção; o segundo método é obter o dia atual da semana para determinar se é uma nova linha K; o terceiro método é principalmente cancelar todas as ordens não preenchidas e obter informações detalhadas da posição, porque antes de a ordem ser colocada, ela deve primeiro determinar o status da posição atual; o quarto método é usado principalmente para imprimir algumas informações, para esta estratégia, este método não é o principal; o mais importante é o quinto método, Este método é principalmente responsável pelo processamento da lógica de negociação e colocação de ordens.
/ / Define the HFT class
Class HFT {
Public:
HFT() {
// Constructor
}
Int getTradingWeekDay() {
// Get the current day of the week to determine if it is a new K line
}
State getState() {
/ / Get order data
}
Void stop() {
// Print orders and positions
}
Bool Loop() {
// Strategy logic and placing orders
}
};
// main function
Void main() {
LogReset(); // clear the log
SetErrorFilter("ready|timeout"); // Filter error messages
Log("Init OK"); // Print the log
HFT hft; // Create HFT object
While (hft.Loop()); // enter loop
Log("Exit"); // Program exits, prints the log
}
Então, vamos ver como cada um dos métodos nesta classe HFT é implementado, e como o método mais básico Loop funciona. De cima para baixo, vamos implementar a implementação específica de cada método um por um, e você vai descobrir que a estratégia de alta frequência original é tão simples. Antes de falar sobre a classe HFT, nós primeiro definimos várias variáveis globais para armazenar os resultados do cálculo do objeto hft. Eles são: armazenar status de ordem, status de posição, manter posição longa, manter posição curta, preço de compra, quantidade de compra, preço de venda, quantidade de venda. Veja o código abaixo:
/ / Define the global enumeration type State
Enum State {
STATE_NA, // store order status
STATE_IDLE, // store position status
STATE_HOLD_LONG, // store long position directions
STATE_HOLD_SHORT, // store short position direction
};
/ / Define global floating point type variable
Typedef struct {
Double bidPrice; // store the buying price
Double bidAmount; // store the buying amount
Double askPrice; // store the selling price
Double askAmount; // store the selling amount
} Book;
Com as variáveis globais acima, podemos armazenar os resultados calculados pelo objeto hft separadamente, o que é conveniente para chamadas subsequentes pelo programa. Em seguida, falaremos sobre a implementação específica de cada método na classe HFT. Primeiro, o primeiro método HFT é um construtor que chama o segundo método getTradingWeekDay e imprime o resultado no log. O segundo método getTradingWeekDay obtém o dia atual da semana para determinar se é uma nova linha K. Também é muito simples de implementar, obter o carimbo horário, calcular a hora e a semana e, finalmente, retornar o número de semanas; o terceiro método getState é um pouco longo, vou apenas descrever a ideia geral, para explicação específica, você pode olhar para os comentários no seguinte bloco de codificação.
Em seguida, vamos obter todas as ordens primeiro, retornar o resultado é uma matriz normal, em seguida, atravessar esta matriz, um por um para cancelar a ordem, em seguida, obter os dados de posição, retornar uma matriz, e depois atravessar esta matriz, obter informações de posição detalhada, incluindo: direção, posição, ontem ou posição atual, etc, e finalmente retornar o resultado; o quarto método de parada é imprimir informações; o código é o seguinte:
Public:
// Constructor
HFT() {
_tradingDay = getTradingWeekDay();
Log("current trading weekday", _tradingDay);
}
// Get the current day of the week to determine if it is a new K line
Int getTradingWeekDay() {
Int seconds = Unix() + 28800; // get the timestamp
Int hour = (seconds/3600)%24; // hour
Int weekDay = (seconds/(60*60*24))%7+4; // week
If (hour > 20) {
weekDay += 1;
}
Return weekDay;
}
/ / Get order data
State getState() {
Auto orders = exchange.GetOrders(); // Get all orders
If (!orders.Valid || orders.size() == 2) { // If there is no order or the length of the order data is equal to 2
Return STATE_NA;
}
Bool foundCover = false; // Temporary variable used to control the cancellation of all unexecuted orders
// Traverse the order array and cancel all unexecuted orders
For (auto &order : orders) {
If (order.Id == _coverId) {
If ((order.Type == ORDER_TYPE_BUY && order.Price < _book.bidPrice - _toleratePrice) ||
(order.Type == ORDER_TYPE_SELL && order.Price > _book.askPrice + _toleratePrice)) {
exchange.CancelOrder(order.Id, "Cancel Cover Order"); // Cancel order based on order ID
_countCancel++;
_countRetry++;
} else {
foundCover = true;
}
} else {
exchange.CancelOrder(order.Id); // Cancel order based on order ID
_countCancel++;
}
}
If (foundCover) {
Return STATE_NA;
}
// Get position data
Auto positions = exchange.GetPosition(); // Get position data
If (!positions.Valid) { // if the position data is empty
Return STATE_NA;
}
// Traverse the position array to get specific position information
For (auto &pos : positions) {
If (pos.ContractType == Symbol) {
_holdPrice = pos.Price;
_holdAmount = pos.Amount;
_holdType = pos.Type;
Return pos.Type == PD_LONG || pos.Type == PD_LONG_YD ? STATE_HOLD_LONG : STATE_HOLD_SHORT;
}
}
Return STATE_IDLE;
}
// Print orders and positions information
Void stop() {
Log(exchange.GetOrders()); // print order
Log(exchange.GetPosition()); // Print position
Log("Stop");
}
Finalmente, nos concentramos em como a função Loop controla a lógica da estratégia e a ordem. Se você quiser ver mais cuidadosamente, você pode consultar os comentários no código. Primeiro, determine se a transação CTP e o servidor do mercado estão conectados; então obtenha o saldo disponível da conta e obtenha o número de semanas; então defina o código de variedade a ser negociado, chamando a função oficial FMZ SetQuantContractType, e pode usar essa função para retornar os detalhes da variedade de negociação; então chame a função GetDepth para obter os dados de profundidade do mercado atual.
// Strategy logic and placing order
Bool Loop() {
If (exchange.IO("status") == 0) { // If the CTP and the quote server are connected
LogStatus(_D(), "Server not connect ...."); // Print information to the status bar
Sleep(1000); // Sleep 1 second
Return true;
}
If (_initBalance == 0) {
_initBalance = _C(exchange.GetAccount).Balance; // Get account balance
}
Auto day = getTradingWeekDay(); // Get the number of weeks
If (day != _tradingDay) {
_tradingDay = day;
_countCancel = 0;
}
// Set the futures contract type and get the contract specific information
If (_ct.is_null()) {
Log(_D(), "subscribe", Symbol); // Print the log
_ct = exchange.SetContractType(Symbol); // Set futures contract type
If (!_ct.is_null()) {
Auto obj = _ct["Commodity"]["CommodityTickSize"];
Int volumeMultiple = 1;
If (obj.is_null()) { // CTP
Obj = _ct["PriceTick"];
volumeMultiple = _ct["VolumeMultiple"];
_exchangeId = _ct["ExchangeID"];
} else { // Esunny
volumeMultiple = _ct["Commodity"]["ContractSize"];
_exchangeId = _ct["Commodity"]["ExchangeNo"];
}
If (obj.is_null() || obj <= 0) {
Panic("PriceTick not found");
}
If (_priceTick < 1) {
exchange.SetPrecision(1, 0); // Set the decimal precision of the price and the quantity of the order.
}
_priceTick = double(obj);
_toleratePrice = _priceTick * TolerateTick;
_ins = _ct["InstrumentID"];
Log(_ins, _exchangeId, "PriceTick:", _priceTick, "VolumeMultiple:", volumeMultiple); // print the log
}
Sleep(1000); // Sleep 1 second
Return true;
}
// Check orders and positions to set status
Auto depth = exchange.GetDepth(); // Get depth data
If (!depth.Valid) { // if no depth data is obtained
LogStatus(_D(), "Market not ready"); // Print status information
Sleep(1000); // Sleep 1 second
Return true;
}
_countTick++;
_preBook = _book;
_book.bidPrice = depth.Bids[0].Price; // "Buying 1" price
_book.bidAmount = depth.Bids[0].Amount; // "Buying 1" amount
_book.askPrice = depth.Asks[0].Price; // "Selling 1" price
_book.askAmount = depth.Asks[0].Amount; // "Selling 1" amount
// Determine the state of the port data assignment
If (_preBook.bidAmount == 0) {
Return true;
}
Auto st = getState(); // get the order data
// Print the port data to the status bar
LogStatus(_D(), _ins, "State:", st,
"Ask:", depth.Asks[0].Price, depth.Asks[0].Amount,
"Bid:", depth.Bids[0].Price, depth.Bids[0].Amount,
"Cancel:", _countCancel,
"Tick:", _countTick);
}
Depois de fazer tanto, podemos finalmente colocar ordens. Antes da negociação, primeiro julgamos o status atual da posição de detenção do programa (nenhuma posição de detenção, ordens de posição longa, ordens de posição curta), aqui usamos o controle de lógica if...else if...else if. Eles são muito simples, Se não houver posição de detenção, a posição será aberta de acordo com a condição lógica. Se houver posição de detenção, a posição será fechada de acordo com a condição lógica. A fim de facilitar a compreensão de todos, usamos três parágrafos para explicar a lógica, Para a parte de posição de abertura:
Primeiro declarar uma variável booleana, usá-lo para controlar a posição de fechamento; em seguida, precisamos obter as informações da conta corrente, e registrar o valor do lucro, em seguida, determinar o status da ordem de retirada, se o número de retirada excede o máximo definido, imprimir as informações relacionadas no log; em seguida, calcular o valor absoluto da oferta atual e oferta diferença de preço para determinar se há mais de 2 saltos entre o preço de oferta atual e o preço de compra.
Em seguida, obtemos o
Bool forceCover = _countRetry >= _retryMax; // Boolean value used to control the number of closings
If (st == STATE_IDLE) { // if there is no holding position
If (_holdAmount > 0) {
If (_countRetry > 0) {
_countLoss++; // failure count
} else {
_countWin++; // success count
}
Auto account = exchange.GetAccount(); // Get account information
If (account.Valid) { // If get account information
LogProfit(_N(account.Balance+account.FrozenBalance-_initBalance, 2), "Win:", _countWin, "Loss:", _countLoss); // Record profit value
}
}
_countRetry = 0;
_holdAmount = 0;
// Judging the status of withdrawal
If (_countCancel > _cancelMax) {
Log("Cancel Exceed", _countCancel); // Print the log
Return false;
}
Bool canDo = false; // temporary variable
If (abs(_book.bidPrice - _book.askPrice) > _priceTick * 1) { // If there is more than 2 hops between the current bid and ask price
canDo = true;
}
If (!canDo) {
Return true;
}
Auto bidPrice = depth.Bids[0].Price; // Buying 1 price
Auto askPrice = depth.Asks[0].Price; // Selling 1 price
Auto bidAmount = 1.0;
Auto askAmount = 1.0;
If (_preBook.bidPrice > _book.bidPrice && _book.askAmount < _book.bidAmount) { // If the previous buying price is greater than the current buying price and the current selling volume is less than the buying volume
bidPrice += _priceTick; // Set the opening long position price
bidAmount = 2; // set the opening long position volume
} else if (_preBook.askPrice < _book.askPrice && _book.bidAmount < _book.askAmount) { // If the previous selling price is less than the current selling price and the current buying volume is less than the selling volume
askPrice -= _priceTick; // set the opening short position volume
askAmount = 2; // set the opening short position volume
} else {
Return true;
}
Log(_book.bidPrice, _book.bidAmount, _book.askPrice, _book.askAmount); // Print current market data
exchange.SetDirection("buy"); // Set the order type to buying long
exchange.Buy(bidPrice, bidAmount); // buying long and open position
exchange.SetDirection("sell"); // Set the order type to selling short
exchange.Sell(askPrice, askAmount); // short sell and open position
}
Em seguida, falaremos sobre como fechar uma posição longa, primeiro definir o tipo de ordem de acordo com o status atual da posição e, em seguida, obter o preço de
Else if (st == STATE_HOLD_LONG) { // if holding long position
exchange.SetDirection((_holdType == PD_LONG && _exchangeId == "SHFE") ? "closebuy_today" : "closebuy"); // Set the order type, and close position
Auto sellPrice = depth.Asks[0].Price; // Get "Selling 1" price
If (sellPrice > _holdPrice) { // If the current "selling 1" price is greater than the long position opening price
Log(_holdPrice, "Hit #ff0000"); // Print long position opening price
sellPrice = _holdPrice + ProfitTick; // Set closing long position price
} else if (sellPrice < _holdPrice) { // If the current "selling 1" price is less than the long position opening price
forceCover = true;
}
If (forceCover) {
Log("StopLoss");
}
_coverId = exchange.Sell(forceCover ? depth.Bids[0].Price : sellPrice, _holdAmount); // close long position
If (!_coverId.Valid) {
Return false;
}
}
Finalmente, vamos ver como fechar a posição curta. O princípio é o oposto da posição longa de fechamento acima mencionada. Primeiro, de acordo com o status da posição atual, defina o tipo de ordem e, em seguida, obtenha o preço de compra 1, se o preço atual de compra 1 for menor que o preço de abertura da posição curta, o preço da posição curta de fechamento será definido. Se o preço atual de compra 1 for maior que o preço da posição curta de abertura, redefine a variável de quantidade de fechamento para verdade, em seguida, feche todas as posições curtas.
Else if (st == STATE_HOLD_SHORT) { // if holding short position
exchange.SetDirection((_holdType == PD_SHORT && _exchangeId == "SHFE") ? "closesell_today" : "closesell"); // Set the order type, and close position
Auto buyPrice = depth.Bids[0].Price; // Get "buying 1" price
If (buyPrice < _holdPrice) { // If the current "buying 1" price is less than the opening short position price
Log(_holdPrice, "Hit #ff0000"); // Print the log
buyPrice = _holdPrice - ProfitTick; // Set the close short position price
} else if (buyPrice > _holdPrice) { // If the current "buying 1" price is greater than the opening short position price
forceCover = true;
}
If (forceCover) {
Log("StopLoss");
}
_coverId = exchange.Buy(forceCover ? depth.Asks[0].Price : buyPrice, _holdAmount); // close short position
If (!_coverId.Valid) {
Return false;
}
}
O texto acima apresenta uma análise completa desta estratégia.https://www.fmz.com/strategy/163427) para copiar o código fonte da estratégia completa sem configurar o ambiente de backtest no FMZ Quant.
Para satisfazer a curiosidade da negociação de alta frequência e ver os resultados mais claramente, esta taxa de transação do ambiente de backtest estratégia é definida em 0, o que leva a uma lógica de velocidade rápida simples. se você quiser cobrir a taxa de transação para alcançar lucratividade no mercado real. Mais otimização é necessária. Como usar o fluxo de pedidos para realizar previsões de curto prazo para melhorar a taxa de ganho, além do reembolso da taxa de câmbio, A fim de alcançar uma estratégia rentável sustentável, existem muitos livros sobre negociação de alta frequência. Espero que todos possam pensar mais e ir ao mercado real em vez de apenas ficar no princípio.
FMZ Quant é uma equipe puramente orientada para a tecnologia que fornece um mecanismo de backtest disponível altamente eficiente para os entusiastas de negociação quantitativa.