Le marché est le champ de bataille, l'acheteur et le vendeur sont toujours dans le jeu, ce qui est également le thème éternel de l'entreprise de trading.
Dans le trading à haute fréquence, il existe deux principaux types de stratégies. La stratégie côté acheteur et la stratégie côté vendeur. La stratégie côté vendeur est généralement une stratégie de création de marché, et les deux côtés de ces stratégies sont opposés. Par exemple, la stratégie acheteur arbitrage à haute fréquence pour lisser tous les phénomènes déraisonnables sur le marché à la vitesse la plus rapide, en prenant l'initiative d'attaquer le prix rapidement, ou en mangeant le mauvais prix d'autres fabricants de marché.
Il existe également un moyen d'analyser les données historiques ou les règles d'ordre du marché, d'envoyer les ordres en attente à un prix déraisonnable à l'avance et d'envoyer les ordres de retrait avec le changement rapide du prix du marché.
Penny Jump signifie "augmentation de micro-prix". Le principe est de suivre le prix d'achat et le prix de vente du marché. Ensuite, selon le prix du marché, plus ou moins l'augmentation de micro-prix du prix de suivi, il est évident que c'est une stratégie de trading passive, elle appartient à la stratégie de prise de marché côté vendeur. Son modèle commercial et sa logique sont de mener des transactions bilatérales sur des ordres de limite cotés en bourse pour fournir de la liquidité.
La stratégie de création de marché nécessite une certaine quantité d'inventaire à la main, puis négocie à la fois du côté de l'acheteur et du vendeur. Le principal revenu de cette stratégie est le rendement des frais de commission fournis par l'échange, ainsi que la différence de prix gagnée en achetant bas et en vendant haut.
Nous savons qu'il y a beaucoup d'investisseurs de détail sur le marché de négociation, et il y a aussi beaucoup de grands investisseurs, tels que:
Si un grand investisseur veut acheter 500 lots de pétrole brut, il n'y a pas autant d'ordres au prix actuel pour vendre, et l'investisseur ne veut pas les acheter au prix plus élevé. s'ils insistent pour envoyer l'ordre d'achat au prix actuel, le coût des points de glissement sera trop élevé. par conséquent, il doit envoyer un ordre en attente au prix souhaité. tous les participants au marché verront un ordre d'achat hugh affichant sur le certain prix.
En raison de cette énorme commande, il semble maladroit sur le marché, parfois nous l'appelons
Selling Price 400.3, Order volume 50; buying price 400.1, Order volume 10.
Soudain, cet éléphant encombrant saute sur le marché, et le prix de l'offre a été envoyé au prix de 400.1.
Selling Price 400.3, Order volume 50; Buying price 400.1, Order volume 510.
Les traders savent tous que s'il y a une énorme quantité d'ordres en attente à un certain prix, alors ce prix va former un fort support ((ou résistance).
Selling Price 400.3, Order volume 50; Buying price 400.2, Order volume 1,
le prix 400.1 devient
Même si le prix n'augmente pas, dans la position d'achat de 2
Tout d'abord, observez les opportunités de trading avec une très faible probabilité du marché, et faites des stratégies correspondantes selon la logique de trading. Si la logique est complexe, vous devez utiliser les connaissances mathématiques existantes, utiliser le modèle pour décrire la nature du phénomène irrationnel autant que possible, et minimiser le surajustement. En outre, il doit être vérifié par un moteur de backtest qui peut répondre au principe
Qu'est-ce que cela signifie de prendre en charge le moteur de backtest
Le volume first est une version améliorée du prix first. Il s'agit à la fois d'un prix prioritaire et d'un temps prioritaire. On peut dire que ce mode de correspondance est exactement le même que le modèle d'échange. En calculant le montant de l'ordre en attente, on juge si l'ordre en attente actuel atteint la condition de transaction passive pour réaliser la transaction en volume, afin d'obtenir une simulation réelle de l'environnement réel du marché.
En outre, certains lecteurs peuvent constater que la stratégie Penny Jump nécessite des opportunités de trading sur le marché, c'est-à-dire que le besoin du marché a au moins deux écarts de prix
La situation de l'apparition de l'écart de prix entre les deux
Ensuite, nous observons la différence entre le
Après avoir dégagé la logique de négociation, nous pouvons utiliser le code pour y parvenir. Puisque la plateforme FMZ Quant utilise C ++ exemples de stratégie d'écriture sont trop peu, ici nous utilisons C ++ pour écrire cette stratégie, ce qui est pratique pour tout le monde à apprendre, et la variété est des contrats à terme sur matières premières.fmz.com> Connexion > Tableau de bord > bibliothèque de stratégie > Nouvelle stratégie > Cliquez sur le menu déroulant dans le coin supérieur gauche > Sélectionnez C++ pour commencer à écrire la stratégie, faites attention aux commentaires dans le code ci-dessous.
Ensuite, examinons la classe HFT, qui a cinq méthodes. La première méthode est la méthode de construction; la deuxième méthode consiste à obtenir le jour actuel de la semaine pour déterminer s'il s'agit d'une nouvelle ligne K; la troisième méthode consiste principalement à annuler tous les ordres non remplis et à obtenir des informations détaillées sur la position, car avant que l'ordre ne soit passé, il doit d'abord déterminer l'état de la position actuelle; la quatrième méthode est principalement utilisée pour imprimer certaines informations, pour cette stratégie, cette méthode n'est pas la principale; la cinquième méthode est la plus importante.
/ / 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
}
Donc, voyons comment chacune des méthodes de cette classe HFT est implémentée, et comment fonctionne la méthode Loop la plus fondamentale. De haut en bas, nous allons implémenter l'implémentation spécifique de chaque méthode une par une, et vous constaterez que la stratégie de haute fréquence originale est si simple. Avant de parler de la classe HFT, nous avons d'abord défini plusieurs variables globales pour stocker les résultats du calcul de l'objet hft. Elles sont: stockage de l'état de l'ordre, l'état de la position, la tenue de la position longue, la tenue de la position courte, le prix d'achat, la quantité d'achat, le prix de vente, la quantité de vente. Veuillez consulter le code ci-dessous:
/ / 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;
Avec les variables globales ci-dessus, nous pouvons stocker les résultats calculés par l'objet hft séparément, ce qui est pratique pour les appels ultérieurs par le programme. Ensuite, nous parlerons de la mise en œuvre spécifique de chaque méthode dans la classe HFT. Premièrement, la première méthode HFT est un constructeur qui appelle la deuxième méthode getTradingWeekDay et imprime le résultat sur le journal. La deuxième méthode getTradingWeekDay obtient le jour actuel de la semaine pour déterminer si c'est une nouvelle ligne K. Elle est également très simple à mettre en œuvre, obtenir l'horodatage, calculer l'heure et la semaine, et enfin retourner le nombre de semaines; la troisième méthode getState est un peu longue, je vais juste décrire l'idée générale, pour une explication spécifique, vous pouvez regarder les commentaires dans le bloc de codage suivant.
Ensuite, obtenons d'abord tous les ordres, renvoyons le résultat est un tableau normal, puis traversons ce tableau, un par un pour annuler l'ordre, puis obtenons les données de position, renvoyons un tableau, puis traversons ce tableau, obtenons des informations de position détaillées, y compris: direction, position, position d'hier ou actuelle, etc., et finalement renvoyons le résultat; la quatrième méthode d'arrêt est d'imprimer des informations; le code est le suivant:
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");
}
Enfin, nous nous concentrons sur la façon dont la fonction Loop contrôle la logique de la stratégie et l'ordre. Si vous voulez voir plus attentivement, vous pouvez consulter les commentaires dans le code. Déterminez d'abord si la transaction CTP et le serveur de marché sont connectés; puis obtenez le solde disponible du compte et obtenez le nombre de semaines; puis définissez le code de variété à échanger, en appelant la fonction officielle SetContractType de FMZ Quant, et pouvez utiliser cette fonction pour retourner les détails de la variété de trading; puis appelez la fonction GetDepth pour obtenir les données de profondeur du marché actuel. Les données de profondeur comprennent: prix d'achat, volume d'achat, prix de vente, volume de vente, etc., et nous les stockons avec des variables, car elles seront utilisées plus tard; Ensuite, sortez ces données vers la barre d'état pour faciliter à l'utilisateur de voir l'état actuel du marché; le code est le suivant:
// 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);
}
Après avoir fait tant, nous pouvons enfin placer des ordres. Avant le trading, nous jugeons d'abord l'état actuel de la position de détention du programme (pas de position de détention, des ordres de position longue, des ordres de position courte), ici nous avons utilisé le contrôle logique if...else if...else if. Ils sont très simples, s'il n'y a pas de position de détention, la position sera ouverte selon la condition logique. s'il y a une position de détention, la position sera fermée selon la condition logique. Afin de faciliter la compréhension de tout le monde, nous utilisons trois paragraphes pour expliquer la logique, pour la partie de position d'ouverture:
Tout d'abord, nous déclarons une variable booléenne, nous l'utilisons pour contrôler la position de clôture; ensuite, nous devons obtenir les informations du compte courant, et enregistrer la valeur du profit, puis déterminer l'état de l'ordre de retrait, si le nombre de retraits dépasse le maximum défini, imprimer les informations connexes dans le journal; puis calculer la valeur absolue de la différence de prix d'offre et d'offre actuelle pour déterminer s'il y a plus de 2 sauts entre le prix d'offre actuel et le prix de demande.
Ensuite, nous obtenons le prix d'achat 1
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
}
Ensuite, nous parlerons de la façon de fermer une position longue, en définissant d'abord le type d'ordre en fonction de l'état de la position actuelle, puis en obtenant le prix 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;
}
}
Enfin, voyons comment fermer une position courte. Le principe est l'opposé de la position longue de clôture mentionnée ci-dessus. Tout d'abord, selon l'état de la position actuelle, définissez le type d'ordre, puis obtenez le prix d'achat 1
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;
}
}
Ce qui précède est une analyse complète de cette stratégie.https://www.fmz.com/strategy/163427) pour copier le code source de la stratégie complète sans configurer l'environnement de backtest sur FMZ Quant.
Afin de satisfaire la curiosité du trading à haute fréquence et de voir les résultats plus clairement, ce coût de transaction de l'environnement de backtest de stratégie est défini à 0, ce qui conduit à une logique simple de vitesse rapide. si vous voulez couvrir le coût de transaction pour atteindre la rentabilité sur le marché réel. Plus d'optimisation est nécessaire. Par exemple, en utilisant le flux de commandes pour effectuer des prévisions à court terme pour améliorer le taux de gain, plus le remboursement des frais de change, Afin d'atteindre une stratégie rentable durable, il existe de nombreux livres sur le trading à haute fréquence. J'espère que tout le monde peut réfléchir davantage et aller sur le marché réel au lieu de simplement rester sur le principe.
FMZ Quant est une équipe purement axée sur la technologie qui fournit un mécanisme de backtest disponible très efficace pour les amateurs de trading quantitatif.