High frequency trading strategy analysis - Penny Jump

Author: The Little Dream, Created: 2023-11-03 17:36:56, Updated: 2023-11-03 22:19:32

img

High-frequency trading is a challenging and competitive field that relies on fast trade execution and sensitive insights into the microstructure of the market. One of the most popular strategies is the Penny Jump, which focuses on leveraging the "elephant" in the market for small but frequent profits. In this article, we will explain in detail how the Penny Jump strategy works, while also delving into the details of the strategy code to help beginners understand how it works.

Understanding the Penny Jump Strategy

In the stock market, "elephant" usually refers to institutional investors who wish to buy or sell a large number of stocks but are unwilling to trade at the market price. Instead, they choose to display a large number of quotes, or quotes, in the market to indicate their intention. This behavior has generated widespread concern in the market because large volume trades can have a significant impact on the market.

For example, let's say the market depth of a stock was 200: $1.01 x $1.03; $200: $200; then an elephant comes in and hangs a bill of $1.01 for 3,000 shares; at this point, the market depth will be 3,200: $1.01 x $1.03; 200: this is like introducing an elephant, which becomes the focus of the other players in the market.

  • Competition market For high-frequency traders, their profits come mainly from analyzing the microstructure of the market to speculate on the intentions of other traders. Once an elephant appears, high-frequency traders will quickly establish positions to capture tiny price fluctuations. Their goal is to trade frequently in a short period of time, accumulating small but cumulative profits.

  • The plight of elephants Although elephants may want to operate in the market on a large scale, their behavior also exposes their trading intentions, making them targets of high-frequency traders. High-frequency traders will attempt to establish positions in advance and then profit from price fluctuations. The presence of elephants in the market may trigger a competitive market response, which affects their trading strategy.

  • Cheating in the Marketplace In reality, large institutional investors usually do not openly and boldly post large amounts of buy or sell orders in the market, because such behavior may lead to countermeasures or even manipulation by other market participants. Therefore, they may employ a strategy to create a false impression, attract high-frequency traders, and then quickly sell or buy to reap a profit from price fluctuations.

The core idea of the Penny Jump strategy

The central idea of the penny jump strategy is that once an elephant in the market appears and supports a particular price (e.g. $1.01), high-frequency traders will quickly raise their bid by one cent, e.g. to $1.02. This is because high-frequency traders understand that an elephant's appearance means that the price has strong buy-side support, so they try to follow suit in the expectation that the price will rise. When the price does rise to $1.03 x $1.05, high-frequency traders can quickly sell, making a profit of $0.01.

Not only that, but high-frequency traders can also make a profit after buying, even if the price has not risen. Because they know that the elephant is supporting the underlying price, they can quickly sell the stock to the elephant for a small margin profit.

Parsing the Penny Jump policy code

The source code of the strategy:https://www.fmz.com/strategy/358

The policy code provided above is an example of an implementation of the Penny Jump strategy. The following is a detailed explanation of the code so that beginners can understand how it works:

var Counter = {
    i: 0,
    w: 0,
    f: 0
};

// Variables
var InitAccount = null;

function CancelAll() {
    while (true) {
        var orders = _C(exchange.GetOrders);
        if (orders.length == 0) {
            break;
        }
        for (var i = 0; i < orders.length; i++) {
            exchange.CancelOrder(orders[i].Id);
        }
        Sleep(Interval);
    }
}

function updateStatus(msg) {
    LogStatus("调戏次数:", Counter.i, "成功:", Counter.w, "失败:", Counter.f, "\n"+msg+"#0000ff\n"+new Date());
}

function main() {
    if (DisableLog) {
        EnableLog(false);
    }
    CancelAll();
    InitAccount = _C(exchange.GetAccount);
    Log(InitAccount);
    var i = 0;
    var locks = 0;
    while (true) {
        Sleep(Interval);
        var depth = _C(exchange.GetDepth);
        if (depth.Asks.length === 0 || depth.Bids.length === 0) {
            continue;
        }
        updateStatus("搜索大象中.... 买一: " + depth.Bids[0].Price + ",  卖一:" + depth.Asks[0].Price + ", 锁定次数: " + locks);
        var askPrice = 0;
        for (i = 0; i < depth.Asks.length; i++) {
            if (depth.Asks[i].Amount >= Lot) {
                askPrice = depth.Asks[i].Price;
                break;
            }
        }
        if (askPrice === 0) {
            continue;
        }
        var elephant = null;
        // skip Bids[0]
        for (i = 1; i < depth.Bids.length; i++) {
            if ((askPrice - depth.Bids[i].Price) > ElephantSpace) {
                break;
            }
            if (depth.Bids[i].Amount >= ElephantAmount) {
                elephant = depth.Bids[i];
                break;
            }
        }

        if (!elephant) {
            locks = 0;
            continue;
        }
        locks++;
        if (locks < LockCount) {
            continue;
        }
        locks = 0;

        updateStatus("调戏大象中....大象在第" + i + "档, " + JSON.stringify(elephant));
        exchange.Buy(elephant.Price + PennyTick, Lot, "Bids[" + i + "]", elephant);
        var ts = new Date().getTime();
        while (true) {
            Sleep(CheckInterval);
            var orders = _C(exchange.GetOrders);
            if (orders.length == 0) {
                break;
            }
            if ((new Date().getTime() - ts) > WaitInterval) {
                for (var i = 0; i < orders.length; i++) {
                    exchange.CancelOrder(orders[i].Id);
                }
            }
        }
        var account = _C(exchange.GetAccount);
        var opAmount = _N(account.Stocks - InitAccount.Stocks);
        if (opAmount < 0.001) {
            Counter.f++;
            Counter.i++;
            continue;
        }
        updateStatus("买单得手: " + opAmount +", 开始出手...");
        exchange.Sell(elephant.Price + (PennyTick * ProfitTick), opAmount);
        var success = true;
        while (true) {
            var depth = _C(exchange.GetDepth);
            if (depth.Bids.length > 0 && depth.Bids[0].Price <= (elephant.Price-(STTick*PennyTick))) {
                success = false;
                updateStatus("没有得手, 开始止损, 当前买一: " + depth.Bids[0].Price);
                CancelAll();
                account = _C(exchange.GetAccount);
                var opAmount = _N(account.Stocks - InitAccount.Stocks);
                if (opAmount < 0.001) {
                    break;
                }
                exchange.Sell(depth.Bids[0].Price, opAmount);
            }
            var orders = _C(exchange.GetOrders);
            if (orders.length === 0) {
                break;
            }
            Sleep(CheckInterval);
        }
        if (success) {
            Counter.w++;
        } else {
            Counter.f++;
        }
        Counter.i++;
        var account = _C(exchange.GetAccount);
        LogProfit(account.Balance - InitAccount.Balance, account);
    }
}

I'll break down the policy code you provided step by step to help you understand how it works in more detail.

var Counter = {
    i: 0,
    w: 0,
    f: 0
};

The code initializes an object called Counter, which is used to track transaction statistics for the policy. Specifically, it includes three attributes:

  • i: Indicates the total number of transactions.
  • w: Indicates the number of successful transactions.
  • f: Indicates the number of failed transactions.

These attributes will be recorded and updated during policy execution.

var InitAccount = null;

This line of code initializes a variable called InitAccount, which will store account information when the policy starts executing.

function CancelAll() {
    while (true) {
        var orders = _C(exchange.GetOrders);
        if (orders.length == 0) {
            break;
        }
        for (var i = 0; i < orders.length; i++) {
            exchange.CancelOrder(orders[i].Id);
        }
        Sleep(Interval);
    }
}

It's calledCancelAll()The function is to cancel all unfinished orders in the market.

  • while (true): This is an infinite loop, and it will continue to execute until there are no unfinished orders.
  • var orders = _C(exchange.GetOrders):这一行代码使用exchange.GetOrders函数获取当前账户所有挂出的订单,并将它们存储在orders变量中。
  • if (orders.length == 0): This line of code checks for unfinished orders. If the length of the order array is 0, it means that there are no unfinished orders, the loop is interrupted (break).
  • for (var i = 0; i < orders.length; i++): This is a for loop, it runs through all unfinished orders.
  • exchange.CancelOrder(orders[i].Id): This line of code uses the exchange.CancelOrder () function to cancel each order using the order's ID.
  • Sleep(Interval): This line of code introduces a waiting cycle, which waits for a period of time (in milliseconds) to ensure that the cancellation operation is not too frequent.

The purpose of this function is to ensure that no unfinished orders exist before the execution of the primary policy, to avoid interfering with the execution of the primary policy.

function updateStatus(msg) {
    LogStatus("调戏次数:", Counter.i, "成功:", Counter.w, "失败:", Counter.f, "\n" + msg + "#0000ff\n" + new Date());
}

It's calledupdateStatus(msg)It accepts an msg parameter, which usually contains information about the current market condition. Specific operations of the function include:

UseLogStatus()The function records the information displayed in the status bar when the policy is running. It displays text about the number of transactions, the number of successes, the number of failures. AddedmsgParameters that contain information about the current state of the market. Added the current time stamp (((new Date()It's also used to display time information. The purpose of this function is to record and update transaction status information for monitoring and analysis during strategy execution.

function main() {
    if (DisableLog) {
        EnableLog(false);
    }
    CancelAll();
    InitAccount = _C(exchange.GetAccount);
    Log(InitAccount);
    var i = 0;
    var locks = 0;
    while (true) {
        Sleep(Interval);
        var depth = _C(exchange.GetDepth);
        if (depth.Asks.length === 0 || depth.Bids.length === 0) {
            continue;
        }
        updateStatus("搜索大象中.... 买一: " + depth.Bids[0].Price + ",  卖一:" + depth.Asks[0].Price + ", 锁定次数: " + locks);
        var askPrice = 0;
        for (i = 0; i < depth.Asks.length; i++) {
            if (depth.Asks[i].Amount >= Lot) {
                askPrice = depth.Asks[i].Price;
                break;
            }
        }
        if (askPrice === 0) {
            continue;
        }
        var elephant = null;
        // skip Bids[0]
        for (i = 1; i < depth.Bids.length; i++) {
            if ((askPrice - depth.Bids[i].Price) > ElephantSpace) {
                break;
            }
            if (depth.Bids[i].Amount >= ElephantAmount) {
                elephant = depth.Bids[i];
                break;
            }
        }

        if (!elephant) {
            locks = 0;
            continue;
        }
        locks++;
        if (locks < LockCount) {
            continue;
        }
        locks = 0;

        updateStatus("调戏大象中....大象在第" + i + "档, " + JSON.stringify(elephant));
        exchange.Buy(elephant.Price + PennyTick, Lot, "Bids[" + i + "]", elephant);
        var ts = new Date().getTime();
        while (true) {
            Sleep(CheckInterval);
            var orders = _C(exchange.GetOrders);
            if (orders.length == 0) {
                break;
            }
            if ((new Date().getTime() - ts) > WaitInterval) {
                for (var i = 0; i < orders.length; i++) {
                    exchange.CancelOrder(orders[i].Id);
                }
            }
        }
        var account = _C(exchange.GetAccount);
        var opAmount = _N(account.Stocks - InitAccount.Stocks);
        if (opAmount < 0.001) {
            Counter.f++;
            Counter.i++;
            continue;
        }
        updateStatus("买单得手: " + opAmount +", 开始出手...");
        exchange.Sell(elephant.Price + (PennyTick * ProfitTick), opAmount);
        var success = true;
        while (true) {
            var depth = _C(exchange.GetDepth);
            if (depth.Bids.length > 0 && depth.Bids[0].Price <= (elephant.Price-(STTick*PennyTick))) {
                success = false;
                updateStatus("没有得手, 开始止损, 当前买一: " + depth.Bids[0].Price);
                CancelAll();
                account = _C(exchange.GetAccount);
                var opAmount = _N(account.Stocks - InitAccount.Stocks);
                if (opAmount < 0.001) {
                    break;
                }
                exchange.Sell(depth.Bids[0].Price, opAmount);
            }
            var orders = _C(exchange.GetOrders);
            if (orders.length === 0) {
                break;
            }
            Sleep(CheckInterval);
        }
        if (success) {
            Counter.w++;
        } else {
            Counter.f++;
        }
        Counter.i++;
        var account = _C(exchange.GetAccount);
        LogProfit(account.Balance - InitAccount.Balance, account);
    }
}

This is the primary executive function of the strategy.main()It contains the core logic of the strategy. Let's explain its operation line by line:

  • if (DisableLog): This line of code checks if the DisableLog variable is true, and if it is, disables log logs. This is to ensure that the policy does not log unnecessary logs.

  • CancelAll(): Call the CancelAll function explained above to ensure that no uncompleted orders exist.

  • InitAccount = _C(exchange.GetAccount): This line of code retrieves information about the current account and stores it in the initAccount variable. This will be used to record the account status when the policy starts executing.

  • var i = 0;andvar locks = 0;: Initializes two variables i and locks, which will be used in subsequent strategy logic.

  • while (true)This is an infinite loop, mainly used for the continuous execution of strategies.

In the next section, we'll explain it line by line.while (true)The main strategic logic within the loop.

while (true) {
    Sleep(Interval);
    var depth = _C(exchange.GetDepth);
    if (depth.Asks.length === 0 || depth.Bids.length === 0) {
        continue;
    }
    updateStatus("搜索大象中.... 买一: " + depth.Bids[0].Price + ",  卖一:" + depth.Asks[0].Price + ", 锁定次数: " + locks);
  • Sleep(Interval): This line of code puts the policy to sleep for a period of time to control the frequency of the policy's execution. The Interval parameter defines the interval of time in which the policy is dormant (in milliseconds).

  • var depth = _C(exchange.GetDepth)• Get current market depth information, including price and quantity of bid and offer. This information is stored in depth variables.

  • if (depth.Asks.length === 0 || depth.Bids.length === 0): This line of code checks for market depth information to ensure that both buy and sell orders exist. If one of these does not exist, indicating that the market may not have enough trade information, the strategy will continue to wait.

  • updateStatus("搜索大象中.... 买一: " + depth.Bids[0].Price + ", 卖一:" + depth.Asks[0].Price + ", 锁定次数: " + locks): This line of code calls the update Status function, updating policy status information. It records the current market status, including a buy, a sell and a previously locked number of locks.

    var askPrice = 0;
    for (i = 0; i < depth.Asks.length; i++) {
        if (depth.Asks[i].Amount >= Lot) {
            askPrice = depth.Asks[i].Price;
            break;
        }
    }
    if (askPrice === 0) {
        continue;
    }
    var elephant = null;

  • var askPrice = 0;: Initialize the askPrice variable, which will be used to store the price of the sold order that meets the conditions.

  • for (i = 0; i < depth.Asks.length; i++): This is a for loop, used to traverse the market for price and quantity information for sales orders.

  • if (depth.Asks[i].Amount >= Lot): In the loop, check if the number of each order is greater than or equal to the specified Lot (number of hands) ; if so, store the price of the order in askPrice and end the loop.

  • if (askPrice === 0): If no order is found that meets the condition (askPrice is still 0), the policy will continue to wait and skip the next operation.

  • var elephant = null;: Initialize the elephant variable, which will be used to store payment information identified as the elephant jaw.

    for (i = 1; i < depth.Bids.length; i++) {
        if ((askPrice - depth.Bids[i].Price) > ElephantSpace) {
            break;
        }
        if (depth.Bids[i].Amount >= ElephantAmount) {
            elephant = depth.Bids[i];
            break;
        }
    }

    if (!elephant) {
        locks = 0;
        continue;
    }
    locks++;
    if (locks < LockCount) {
        continue;
    }
    locks = 0;

Continue to browse the price and quantity information of the market bids, skipping the first bid ((Bids[0])).

  • if ((askPrice - depth.Bids[i].Price) > ElephantSpace): check if the gap between the current bid price and the ask price is greater than ElephantSpace. If yes, indicate that you are far enough away from the elephant's nest to stop the search.

  • if (depth.Bids[i].Amount >= ElephantAmount): checks whether the number of current payments is greater than or equal to ElephantAmount, and if so, stores the payment information in the elephant variable.

  • if (!elephant): If no elephant lock is found, the number of locks will be reset to 0 and continue to wait.

locks++: Increases the number of locks if the elephant's nest is found. This is to ensure that the policy is executed after multiple confirmations of the elephant's nest have been made over time.

  • if (locks < LockCount): Check if the lock count is met. If not, continue to wait.
    updateStatus("调戏大象中....大象在第" + i + "档, " + JSON.stringify(elephant));
    exchange.Buy(elephant.Price + PennyTick, Lot, "Bids[" + i + "]", elephant);
    var ts = new Date().getTime();
    while (true) {
        Sleep(CheckInterval);
        var orders = _C(exchange.GetOrders);
        if (orders.length == 0) {
            break;
        }
        if ((new Date().getTime() - ts) > WaitInterval) {
            for (var i = 0; i < orders.length; i++) {
                exchange.CancelOrder(orders[i].Id);
            }
        }
    }

  • updateStatus("调戏大象中....大象在第" + i + "档, " + JSON.stringify(elephant)): Calls the update Status function, which records the current status of the policy, including the rank of the elephant's nest found and related information. This will be displayed in the policy's status bar.

  • exchange.Buy(elephant.Price + PennyTick, Lot, "Bids[" + i + "]", elephant): use the exchange.Buy function to buy found elephant elephants. The purchase price is elephant. Price + PennyTick, the purchase quantity is Lot, and the purchase operation is described as "Bids[" + i + ] elephant elephants".

  • var ts = new Date().getTime(): Obtain the time stamp of the current time for subsequent calculation of time intervals.

  • while (true): entering a new infinite loop, used to execute pending orders to buy elephant tusks.

  • Sleep(CheckInterval)The policy is dormant for a period of time to control the frequency of checking the order status.

  • var orders = _C(exchange.GetOrders)You can access all the order information from your current account.

  • if (orders.length == 0): check for unfinished orders, if not, jump out of the loop.

  • (new Date().getTime() - ts) > WaitInterval: Calculates the time interval between the current time and the time when you bought the elephant shell, if it exceeds the Wait Interval, it indicates that the wait time is over.

  • for (var i = 0; i < orders.length; i++)I'm going to go through all the unfinished orders.

  • exchange.CancelOrder(orders[i].Id):使用exchange.CancelOrder函数取消每个未完成的订单。

    var account = _C(exchange.GetAccount);
    var opAmount = _N(account.Stocks - InitAccount.Stocks);
    if (opAmount < 0.001) {
        Counter.f++;
        Counter.i++;
        continue;
    }
    updateStatus("买单得手: " + opAmount + ", 开始出手...");
    exchange.Sell(elephant.Price + (PennyTick * ProfitTick), opAmount);
    var success = true;
    while (true) {
        var depth = _C(exchange.GetDepth);
        if (depth.Bids.length > 0 && depth.Bids[0].Price <= (elephant.Price - (STTick * PennyTick))) {
            success = false;
            updateStatus("没有得手, 开始止损, 当前买一: " + depth.Bids[0].Price);
            CancelAll();
            account = _C(exchange.GetAccount);
            var opAmount = _N(account.Stocks - InitAccount.Stocks);
            if (opAmount < 0.001) {
                break;
            }
            exchange.Sell(depth.Bids[0].Price, opAmount);
        }
        var orders = _C(exchange.GetOrders);
        if (orders.length === 0) {
            break;
        }
        Sleep(CheckInterval);
    }
    if (success) {
        Counter.w++;
    } else {
        Counter.f++;
    }
    Counter.i++;
    var account = _C(exchange.GetAccount);
    LogProfit(account.Balance - InitAccount.Balance, account);
}

  • var account = _C(exchange.GetAccount)You can access the following information:

  • var opAmount = _N(account.Stocks - InitAccount.Stocks)If the change is less than 0.001, the purchase fails, the number of failures is increased and the next cycle continues.

  • updateStatus("买单得手: " + opAmount + ", 开始出手...")The following is a list of successful purchases of ivory, including the number of purchases:

  • exchange.Sell(elephant.Price + (PennyTick * ProfitTick), opAmount): use the exchange.Sell function to sell successfully purchased elephant tusks for a profit. Sell at elephant.Price + (PennyTick * ProfitTick)

Enter into a new, infinite loop, used to execute pending sales orders.

  • var depth = _C(exchange.GetDepth)It's a great way to get in-depth information about the market.

  • if (depth.Bids.length > 0 && depth.Bids[0].Price <= (elephant.Price - (STTick * PennyTick)))Check the market depth information and execute the stop loss operation if the market price has fallen to stop loss.

  • CancelAll()Call the CancelAll () function to cancel all unfinished orders to avoid holding risk.

  • if (opAmount < 0.001): check the number of purchases again, if less than 0.001, the purchase failed and jumped out of the loop.

  • exchange.Sell(depth.Bids[0].Price, opAmount)In the case of the sale of the remaining assets at the lowest current market price, a stop-loss operation is carried out.

Finally, the number of successful and failed trades is updated according to whether the trades are successful or not, and the profit is recorded.

This is a line-by-line explanation of the whole strategy. The central idea of this strategy is to find the elephant in the market for a lot of money and buy and sell it for a small profit. It includes several important parameters, such as the number of purchases (Lot), the error-retest interval (Interval), the elephant amount (ElephantAmount), the elephant distance (ElephantSpace) and so on, to adjust the behavior of the strategy.

Overall, this strategy is a high-frequency trading strategy that is designed to leverage market depth information to identify large amounts of pay-offs and make short-term trades. It requires constant monitoring of the market and execution of short-term trades in order to quickly reap small profits. However, it is also a high-risk strategy because it requires quick response to market fluctuations, while considering risk management and stop-loss mechanisms to avoid large losses.

Please note that this strategy is based on specific markets and trading platforms and may need to be adjusted and optimized accordingly for different markets and exchanges. In practical applications, investors need to carefully test and evaluate the performance of the strategy to ensure that it meets their investment objectives and risk tolerance.

As you continue to execute the policy, it repeatedly executes the following in a loop:

First, the strategy examines deep market information to understand the current bid and offer conditions.

2, Next, the strategy will try to find eligible bids, specifically if the number of bids is greater than or equal to Lot (number of hands). If eligible bids are found, the price of the bid is recorded as askPrice.

3, the strategy then proceeds to search for the elephant shell (a lot of payments); it goes through the market and skips the first payment (usually the highest price payment); if it finds a qualified elephant shell, it records the information of the elephant shell and increases the number of locks.

4, if a sufficient number of consecutive elephant ticks (controlled by the LockCount parameter) are found, the policy will further execute the following operation:

  • Call the update Status function to record the status of the elephant's nest and related information.
  • Use the exchange.Buy function to buy an elephant with an elephant.Price + PennyTick with a lot.
  • Starting a new infinite loop, used to execute pending purchase orders.
  • Check the order status, if the order is completed, jump out of the loop.
  • If the waiting time exceeds the set wait interval, all uncompleted orders are canceled.
  • Calculates the change in the account asset after a successful purchase, if the change is less than 0.001, indicates a failed purchase, increases the number of failures, and continues the next cycle.
  • The information on successful purchases of elephant tusks, including the number of purchases, is recorded.

5, the policy then proceeds to a new infinite loop, which is used to wait for the execution of the sell operation. In this loop, it performs the following operations:

  • It is also possible to get deep market information and check if the market price has reached the stop loss price.
  • If the market price has reached or is below the stop loss price, a stop loss operation is performed, i.e. the remaining assets are sold.
  • Call the CancelAll function to cancel all pending orders to reduce the risk of holding.
  • Re-check the change in the account asset after the successful purchase, if the change is less than 0.001, the purchase failed and jumped out of the loop.
  • Lastly, it records whether the transaction is successful or not, and updates the number of successful and failed transactions based on the results.

The entire strategy is a continuous cycle of executing the above operations in order to capture as much of the elephant's trunk as possible and make tiny profits. This is a high-frequency trading strategy that requires a quick response to market changes while considering risk management and stop-loss mechanisms to protect capital. Investors should consider using this strategy carefully, especially in highly volatile markets.

The ending

The penny jump strategy is a classic example of high-frequency trading, which shows the subtle play and competition between market participants. In the cryptocurrency market, this strategy is particularly prominent because of the high volatility of the market, both institutional investors and high-frequency traders seeking quick profits. However, it also makes the market challenging and requires constant adaptation and adjustment of strategies to maintain a competitive advantage.


More