시장은 전투 현장이며, 구매자와 판매자는 항상 게임에 있으며, 이는 또한 거래 비즈니스의 영원한 주제입니다. 오늘날 공유되는 페니 점프 전략은 고주파 전략 중 하나이며, 원래 은행 간 외환 시장에서 파생되었으며 주류 팩트 통화 쌍에서 자주 사용됩니다.
고주파 거래에는 두 가지 주요 유형의 전략이 있습니다. 구매자 측면 전략과 판매자 측면 전략. 판매자 측면 전략은 일반적으로 시장 제작 전략이며, 이 두 측면 전략은 반대입니다. 예를 들어, 고주파 중재 구매자 전략은 시장의 모든 불합리한 현상을 가장 빠른 속도로 부드럽게하고, 가격을 신속하게 공격하거나 다른 시장 제작자의 잘못된 가격을 먹는 것입니다.
또한 역사적인 데이터 또는 시장의 주문 규칙을 분석하고, 부득이한 가격으로 대기 주문을 사전에 보내고, 시장 가격의 급격한 변화와 함께 철수 명령을 보내는 방법이 있습니다. 이러한 전략은 수동 시장 제작에서 일반적입니다. 대기 주문이 실행되면 특정 이익 또는 스톱 로스 조건에 도달한 후 포지션은 닫힐 것입니다. 수동 시장 제작 전략은 일반적으로 너무 많은 속도를 필요로하지 않지만 강력한 전략 논리와 구조가 필요합니다.
페니 점프는 영어로 번역하면 마이크로 가격 상승의 의미입니다. 원칙은 시장의 구매 가격과 판매 가격을 추적하는 것입니다. 그러면 시장 가격에 따라, 추적 가격의 마이크로 가격 상승을 더하거나 빼면 이것은 수동적인 거래 전략이며 판매자 측 시장 제작 전략에 속한다는 것이 분명합니다. 비즈니스 모델과 논리는 유동성을 제공하기 위해 거래소 상장 한도 주문에 대한 양자 거래를 수행하는 것입니다.
시장 제작 전략은 손에 일정 양의 재고를 요구하고 구매자 및 판매자 양쪽에서 거래합니다. 이 전략의 주요 수입은 거래소가 제공하는 수수료 수익이며, 낮은 가격과 높은 가격으로 구매하여 얻은 가격 차이입니다. 그러나 시장 제작을 원하는 많은 고주파 거래자에게 입찰-구매 스프레드를 얻는 것은 좋은 일이지만 절대적인 수익 수단은 아닙니다.
우리는 거래 시장에 많은 소매 투자자가 있고, 또한 많은 대형 투자자가 있다는 것을 알고 있습니다. 예를 들어:
큰 투자자가 500 롯의 원유를 구입하고 싶다면, 판매를 위해 현재 가격에 많은 주문이 없으며, 투자자는 더 높은 가격으로 구입하기를 원하지 않습니다. 현재 가격에 구매 주문을 보내기를 주장하면, 미끄러짐 포인트의 비용은 너무 높습니다. 따라서 원하는 가격에 미뤄있는 주문을 보내야합니다. 시장의 모든 참가자는 특정 가격에 표시되는 높은 구매 주문을 볼 것입니다.
이 거대한 주문 때문에 시장에서 어색하게 보입니다. 때때로 우리는 그것을 "코끼리 주문"이라고 부릅니다. 예를 들어, 현재 시장은 다음과 같습니다.
Selling Price 400.3, Order volume 50; buying price 400.1, Order volume 10.
갑자기 이 번거로운 코끼리가 시장에 뛰어들었고, 입찰 가격은 400.1의 가격으로 보내졌습니다. 이 시점에서 시장은:
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를 구매하는 위치에서는 여전히
먼저, 시장의 매우 낮은 확률로 거래 기회를 관찰하고, 거래 논리에 따라 대응 전략을 세워야 합니다. 논리가 복잡하다면, 기존의 수학적 지식을 사용하여 모형을 사용하여 비합리 현상의 본질을 가능한 한 설명하고, 과잉 적합성을 최소화해야 합니다. 또한, 이것은
또한 일부 독자들은 페니 점프 전략은 시장 거래 기회를 필요로한다는 것을 발견할 수 있습니다. 즉, 시장의 필요성은 적어도 두 개의
1
두 개의
다음으로, 우리는 이전
거래 논리를 정리 한 후, 우리는 그것을 달성하기 위해 코드를 사용할 수 있습니다. FMZ 퀀트 플랫폼이 C ++ 작성 전략을 사용하는 예가 너무 적기 때문에, 여기 우리는 C ++를 사용하여 모든 사람이 배우기 편리한 이 전략을 작성합니다.fmz.com> 로그인 > 대시보드 > 전략 라이브러리 > 새로운 전략 > 왼쪽 상단에있는 드롭다운 메뉴를 클릭 > 전략을 작성하기 위해 C ++를 선택하십시오. 아래 코드의 댓글에 주의하십시오.
다음으로, 다섯 가지 방법이 있는 HFT 클래스를 살펴보자. 첫 번째 방법은 구성 방법이다; 두 번째 방법은 새로운 K 라인인지 여부를 결정하기 위해 현재 주일을 얻는 방법이다; 세 번째 방법은 주로 모든 채우지 않은 오더를 취소하고 자세한 위치 정보를 얻는 방법이다. 왜냐하면 오더가 배치되기 전에 먼저 현재 위치 상태를 결정해야 하기 때문이다. 네 번째 방법은 주로 일부 정보를 인쇄하는 데 사용됩니다. 이 전략에서는 이 방법이 주요한 방법이 아닙니다. 가장 중요한 것은 다섯 번째 방법입니다. 이 방법은 주로 거래 논리와 오더를 배치하는 데 책임이 있습니다.
/ / 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 클래스의 각 방법들이 어떻게 구현되는지, 그리고 가장 핵심적인 루프 방법이 어떻게 작동하는지를 살펴보자. 위에서 아래로, 각 방법의 구체적인 구현을 하나씩 구현할 것이고, 원래의 고주파 전략이 매우 간단하다는 것을 알게 될 것이다. 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 메소드는 두 번째 getTradingWeekDay 메소드를 호출하고 결과를 로그에 프린트하는 컨스트럭터이다. 두 번째 getTradingWeekDay 메소드는 새로운 K 라인인지 여부를 결정하기 위해 현재 주일을 얻는다. 또한 구현하는 것은 매우 간단하다. 타임 스탬프를 얻고, 시간과 주를 계산하고, 마지막으로 주 수를 반환한다. 세 번째 getState 메소드는 조금 길어, 나는 단지 일반적인 아이디어를 설명할 것이다. 구체적인 설명에 대해서는 다음 코딩 블록의 댓글을 살펴볼 수 있다.
다음으로, 먼저 모든 순서를 받아서, 결과를 정상 배열로 반환하고, 그 다음이 배열을 가로질러, 하나씩 순서를 취소하고, 위치 데이터를 얻고, 배열을 반환하고, 그 다음이 배열을 가로질러, 방향, 위치, 어제 또는 현재 위치 등을 포함한 자세한 위치 정보를 얻고, 마지막으로 결과를 반환합니다. 네 번째 정지 방법은 정보를 인쇄하는 것입니다. 코드는 다음과 같습니다:
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");
}
마지막으로, 우리는 루프 함수가 전략 논리와 순서를 제어하는 방법에 초점을 맞추고 있습니다. 더 자세히보고 싶다면 코드의 댓글을 참조 할 수 있습니다. 먼저 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 로직 컨트롤을 사용했습니다. 그들은 매우 간단합니다. 만약 보유 위치가 없다면, 로직 조건에 따라 포지션이 열릴 것입니다. 만약 보유 위치가 있다면, 로직 조건에 따라 포지션이 닫힐 것입니다. 모든 사람의 이해를 돕기 위해, 우리는 논리를 설명하기 위해 세 개의 문장을 사용합니다.
먼저 볼변수를 선언하고, 우리는 폐쇄 포지션을 제어하기 위해 그것을 사용합니다. 다음으로 우리는 현금 계좌 정보를 얻고, 이익 가치를 기록해야 합니다. 그 다음 인출 주문의 상태를 결정하고, 인출 수가 설정된 최대치를 초과하면 관련 정보를 로그에 인쇄합니다.
다음으로, 우리는
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/163427) FMZ Quant에서 백테스트 환경을 구성하지 않고 전체 전략 소스 코드를 복사합니다.
높은 주파수 거래의 호기심을 충족시키고 결과를 더 명확하게 보기 위해,이 전략 백테스트 환경 거래 수수료는 0으로 설정되어 간단한 빠른 속도 논리로 이어집니다. 실제 시장에서 수익성을 달성하기 위해 거래 수수료를 커버하려면. 더 많은 최적화가 필요합니다. 예를 들어, 승률을 향상시키기 위해 단기 예측을 수행하기 위해 주문 흐름을 사용하는 것과 교환 수수료 환불, 지속 가능한 수익성있는 전략을 달성하기 위해, 높은 주파수 거래에 대한 많은 책이 있습니다. 모든 사람이 더 많이 생각하고 원칙에 머무르지 않고 실제 시장으로 갈 수 있기를 바랍니다.
FMZ 퀀트는 정량 거래 애호가들에게 매우 효율적인 백테스트 메커니즘을 제공하는 순수 기술 중심의 팀입니다. 우리의 백테스트 메커니즘은 단순한 가격 매치보다는 실제 교환을 시뮬레이션합니다. 사용자는 자신의 능력을 더 잘 발휘하기 위해 플랫폼을 활용할 수 있기를 바랍니다.