市場は戦場であり,買い手と売り手は常にゲームの中にいる.これはまた,取引ビジネスの永遠のテーマである.今日共有されているペニージャンプ戦略は,元々銀行間為替市場から派生した高周波戦略の1つであり,主流のファイト通貨ペアでしばしば使用されています.
高周波取引には2つの主要なタイプの戦略があります. バイヤーサイド戦略とセラーサイド戦略. バイヤーサイド戦略は通常,マーケットメーキング戦略であり,この2つの戦略の側面は反対です. 例えば,高周波仲介は,市場のすべての不合理な現象を最も速い速度で平滑させ,価格を迅速に攻撃するイニシアティブをとり,または他のマーケットメーカーの間違った価格を食べるバイヤー戦略です.
また,過去データや市場のオーダールールを分析し,不合理な価格で待機しているオーダーを事前に送信し,市場の価格の急速な変化に伴い引き出すオーダーを送信する方法もあります.このような戦略は受動的なマーケットメーキングで一般的です.待機しているオーダーが実行されると,一定の利益またはストップロスの条件に達した後,ポジションは閉鎖されます.受動的なマーケットメーキング戦略は通常,あまりにも多くの速度を必要としませんが,強力な戦略論理と構造が必要です.
ペニージャンプは,英語に翻訳するとマイクロ価格上昇の意味です.原則は,市場の購入価格と販売価格を追跡することです.その後,市場の価格に応じて,プラスまたはマイナス,追跡価格のマイクロ価格上昇により,これは受動的な取引戦略であり,それは売り手側市場作りの戦略に属しています.そのビジネスモデルと論理は,流動性を提供するために取引所にリストされている制限注文で二国間取引を行うことです.
マーケットメーキング戦略には,ある程度の在庫を手にし,その後,買い手側と売り手側の両方で取引する必要があります.この戦略の主な収入は,取引所によって提供される佣金返金,低価格で購入し,高価格で販売することで得られる価格差です.しかし,マーケットメーキングを望む多くの高周波トレーダーにとって,バイド・アスク・スプレッドを得ることは良いことですが,絶対的な利益の手段ではありません.
取引市場には多くの個人投資家がおり,また,多くの大手投資家がおり,例えば: 熱いお金,公共資金,民間資金,など.個人投資家は通常,資金が少なく,彼らの注文は市場で非常に小さな影響を持ち,いつでも簡単に取引目標を購入して販売することができます. しかし,大きな資金が市場に参画するには,それはそれほど簡単ではありません.
大型投資家が原油の500ロットを購入したい場合,現在の価格で販売するオーダーがあまり多くなく,投資家はより高い価格でそれらを購入したくない.彼らが現在の価格で購入オーダーを送信することを主張した場合,滑り点のコストはあまりにも多くなります.したがって,それは望ましい価格で待機オーダーを送信する必要があります.市場のすべての参加者は,特定の価格に表示されるHugh購入オーダーを見るでしょう.
市場では不器用に見えますが,時には"象の注文"と呼ばれます.
Selling Price 400.3, Order volume 50; buying price 400.1, Order volume 10.
この時,市場が変わります. この時点で,市場が変わります.
Selling Price 400.3, Order volume 50; Buying price 400.1, Order volume 510.
トレーダーは,特定の価格で膨大な量の中止注文がある場合,この価格が強いサポート (またはレジスタンス) を形成することを知っています. さらに,高周波トレーダーは,注文簿深さで
Selling Price 400.3, Order volume 50; Buying price 400.2, Order volume 1,
価格400.1はオーダーブック深さで
価格が上昇しない場合でも,2を購入するポジションでは,価格を保持する"象"が残っており,それは迅速に400の価格で象に売り返せる.これはペニージャンプ戦略の一般的な考えである.その論理は,市場オーダーの状態を監視することによって,相手の意図を推測し,その後有利なポジションを構築し,最終的に短時間で小さなスプレッドから利益を得るためにリードする.この"象"のために,彼は市場で膨大な量の購入オーダーを吊るしているため,彼の取引の意図を暴露し,自然に高周波トレーダーの狩猟対象となった.
まず,市場の非常に低い確率で取引機会を観察し,取引論理に従って対応する戦略を作成します.論理が複雑であれば,既存の数学知識を使用して,不合理現象の性質をできるだけ記述し,オーバーフィッティングを最小限にするためにモデルを使用する必要があります. さらに,このバックテストモードをサポートしているFMZ Quantプラットフォームがあります.
支持の意味は? 価格先でボリューム先
さらに,ペニージャンプ戦略には市場取引機会が必要である,つまり市場需要には少なくとも2つの
価格格差の出現は非常に頻繁に起こりますが,3つのホップは最も安全ですが,3つのホップはめったに起こらないため,取引頻度は低すぎます.
次に,前回の"販売"と"購入"と"販売"の違いを観察します.市場間の価格ギャップを埋めるために,スピードが十分に速い場合は,他のオーダーの最前線に置くことができます.また,ポジション保持時間が非常に短く,この取引論理で,戦略の実現後,MA909を例として,実際の市場テストは,CTPインターフェースの代わりにEsunnyを推奨します.Esunnyのポジションとファンド状況の変化のメカニズムはプッシュデータによって,高周波取引に非常に適しています.
FMZ Quant プラットフォームは C++ を使っているため,この戦略の記述例は少ないので,ここで C++ を使ってこの戦略を書きます.これは誰もが学ぶのに便利で,その多様性は商品先物です.最初に開きます:fmz.com> ログイン > ダッシュボード > 戦略ライブラリ > 新しい戦略 > 左上のドロップダウンメニューをクリック > C++ を選択して戦略を書き始めます.
次に,HFTクラスを見てみましょう.このクラスには5つの方法があります.最初の方法はコンストラクション方法です.第二の方法は,新しいKラインかどうかを判断するために,週の現在の日を取得することです.第三の方法は,主にすべての未完了注文をキャンセルし,詳細なポジション情報を取得することです.注文を出す前に,まず現在のポジションの状態を決定する必要があります.第四の方法は,主にいくつかの情報を印刷するために使用されます.この戦略では,この方法はメインではありません.最も重要なのは第5の方法です.この方法は,主に取引論理と注文の処理を担当しています.
/ / 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
}
この HFT クラスの各メソッドがどのように実装されているか,最も基本的な Loop メソッドがどのように機能しているかを見てみましょう.上から下へと,各メソッドの特定の実装を一つずつ実装し,元の高周波戦略が非常にシンプルであることがわかります. HFT クラスについて話す前に,私たちはまずhft オブジェクト計算の結果を保存するためのいくつかのグローバル変数を定義しました.これらは:オーダーの状態を保存する,ポジションの状態,ロングポジションを保持する,ショートポジションを保持する,購入価格,購入量,販売価格,販売量.以下のコードを参照してください:
/ / 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;
上記のグローバル変数で, hft オブジェクトによって計算された結果を別々に保存することができます.これはプログラムによる次の呼び出しに便利です.次に,HFT クラス内の各メソッドの特定の実装についてお話します.まず,最初の HFT メソッドは,第2の getTradingWeekDay メソッドを呼び,結果をログにプリントするコンストラクターです.第2の getTradingWeekDay メソッドは,それが新しい K 行であるかどうかを判断するために,週の現在の日を取得します.実装することも非常に簡単です.タイムスタンプを取得し,時間と週を計算し,最後に週の数を返す.第3の getState メソッドは少し長く,私は一般的なアイデアを説明します.具体的な説明のために,次のコーディングブロックのコメントを見ることができます.
次に,すべての順序を最初に取得し,結果は正規配列を返し,次にこの配列を1つずつ通過し,順序をキャンセルし,その後位置データを取得し,配列を返し,次にこの配列を通過し,詳細な位置情報を取得します. 方向,位置,昨日の位置,現在の位置など,そして最後に結果を返します. 第4のストップ方法は情報を印刷することです. コードは以下のとおりです:
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");
}
最後に,Loop関数が戦略論理と順序をどのように制御するかに焦点を当てます.もっと注意深く見たい場合は,コードのコメントを参照できます.まず,CTP取引とマーケットサーバーが接続されているかどうかを決定します.その後,アカウントの利用可能な残高を取得し,週の数を取得します.その後,FMZ公式のSetQuantContractType関数に呼び出し,取引される多様性コードを設定し,この関数を使用して取引多様性の詳細を返します.その後,現在の市場の詳細を取得するためにGetDepth関数を呼びましょう.深さのデータには:購入価格,購入量,販売価格,販売量などが含まれ,後で使用されるため,変数とともに保存します.その後,このポートデータをステータスバーに出力して,ユーザーが現在の市場状態を表示することを容易にする.コードは以下のとおりです:
// 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);
}
取引前に,まず,プログラムの現在の保持ポジションの状態 (保持ポジションなし,ロングポジションオーダー,ショートポジションオーダー) を判断します.ここで if...else if...else if ロジック制御を使用しました. 非常にシンプルです.保持ポジションがない場合は,論理条件に従ってポジションが開かれます.保持ポジションがある場合は,論理条件に従ってポジションが閉まります.誰もが理解できるように,論理を説明するために,3つの段落を使用します. ポジション開設部分:
まずブール変数を宣言し,閉店ポジションを制御するためにそれを使用します. 次に,当日口座情報を取得し,利益値を記録し,その後,引き出すオーダーの状態を決定します. 引き出す数が設定された最大値を超えると,ログに関連する情報を印刷します. その後,現在の入札と出札価格の絶対値を計算して,現在の入札価格と出札価格の間の2回以上のホップがあるかどうかを決定します.
次に,前回の購入価格が現在の購入価格よりも高く,現在の販売量は購入量よりも少ない場合,前回の購入価格が消えたことを意味します.ロングポジション開設価格と注文量が設定されています.そうでなければ,前回の販売価格が現在の販売価格よりも低く,現在の購入量は以下です.販売量は,前回の販売価格が現在の販売価格よりも低く,現在の購入量は以下です.販売量は,前回の購入価格が現在の購入価格よりも高く,現在の販売量は購入量よりも少ない場合は,前回の購入価格が現在の購入価格よりも高く,現在の販売量は購入量よりも少ない場合は,それは"ロングポジション開設価格"と"ショートポジション開設価格"と"ショートポジション注文量"が設定されていることを証明します.最後に,ロングポジションとショートポジションが同時に市場に参入します.具体的なコードは以下のとおりです:
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
}
次に,ロングポジションを閉じる方法について説明します.まず,現在のポジションの状態に応じてオーダータイプを設定し,その後,
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;
}
}
最後に,ショートポジションを閉じる方法を見ていきましょう.原則は上記の閉じるロングポジションの反対です.まず,現在のポジションの状態に応じて,オーダータイプを設定し,その後,現在の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;
}
}
この戦略の完全な分析は,上記のとおりです.ここをクリックしてください (https://www.fmz.com/strategy/163427FMZ Quant のバックテスト環境を設定することなく,完全な戦略ソースコードをコピーできます.
高周波取引の好奇心を満たし,結果をより明確に確認するために,この戦略バックテスト環境の取引手数料は0に設定され,単純な高速論理につながります.もし実際の市場での収益性を達成するために取引手数料をカバーしたい場合は,より多くの最適化が必要です.例えば,オーダーストリームを使用して,短期予測を行い,勝利率を向上させ,交換手数料の返済を加えるように,持続的な収益性の高い戦略を達成するために,高周波取引に関する多くの本があります.誰もがより多くのことを考え,原則にとどまるのではなく,実際の市場に行くことを願っています.
FMZ Quantは,純粋に技術主導のチームで,定量的な取引の熱狂的な人々のために非常に効率的なバックテストメカニズムを提供しています. 私たちのバックテストメカニズムは,単純な価格マッチではなく,実際の取引をシミュレートしています. ユーザーがプラットフォームを利用して自分の能力をよりよく発揮できることを願っています.