💹Performance tuning

Make sure you already have the bot running and landing before diving into this page. This page assumes you already have some basic knowledge of the bot.

Once you get your bot up and running, the next step is to tune it to get the best performance. There are a lot of variables so the exact config may differ for every person. This page will go through all the things that may affect the performance, and how you can improve it.

When an arb opportunity appears, there will be hundreds or thousands of bots trying to seize it. And you want to be faster than others and be the first few ones to take it. So the overall idea is very simple, you want to be fast in every possible places. Fast reading, fast sending, fast landing etc. And all the tuning in this page is around this. every milliseconds matters.

RPC

RPC is probably one of the most important things for your setup. There are a few things to consider here.

location

location can affect the performance in several different ways. First you want your server to be as close to your RPC as possible. The easiest way to test is ping your rpc address from your server.

Example:

ping api.mainnet-beta.solana.com

The ideal value for a good location is <10 ms. The most idea location is <1 ms, which basically means your RPC is physically located in the same data center as your server.

Read vs Send

Read and send are actually two totally different things for an RPC. Find a good read RPC to read can make sure you get the opportunity right when it appears, a good send RPC can help you to land the transaction and land it fast.

Read tuning

PROCESS_DELAY

Default is 1000, unit in milliseconds. This controls how long the bot wait to quote and try to find an arb again for a given base mint + trade size. The less of this, the more frequently the bot try to find the arb. Do note that a small number like 100 may kill your RPC or Jup API due to the high load of requests. If you make this smaller and then sees much less or no opportunities, that means it's too small and you need to adjust it.

If you are using public jup api https://quote-api.jup.ag/v6, make sure you set PROCESS_DELAY to a very big number like 10000, with only one base mint and one trade size, this may help you to test it longer before getting blocked.

Send tuning

ENABLE_SPAM

For maximum profit you will want to enable this. This means the bot will send transactions to all the senders you set, some of them may not land, some may land but failed, and some will land and succeed. You will need to find a good RPC and tweak your settings to make sure your profit can cover all the gas fees for failed transactions.

ENABLE_SIMULATION

When this is set to true, all the potential arbs will call a simulationTransaction call to your read RPC, and only the ones that succeeded will be seen as an oppotunity. Running simulation can be very expensive for RPC, so if you have too many pairs of base-mint + trade size, and have a very small PROCESS_DELAY, this can kill your RPC and causing you to see no opportunities at all.

The good part of setting this to true is, this can greatly reduce the number of failed transactions you land compare to set it to false, but in exchange for an additional call to your RPC which can slow down your speed to send transaction.

When setting this to false, and if you have ENABLE_SPAM=true, do expect to see a lot of failed transactions.

SKIP_PREFLIGHT

This is a config you can change when you send your transaction. To understand what it means you can read the Solana doc here. In short the rpc you send your transaction with will simulate your transaction and not sending it if it failed. This is very similar to ENABLE_SIMULATION, but this runs with your send rpcs, and ENABLE_SIMULATION runs with your read rpc.

You should only set at most one of ENABLE_SIMULATION=true and SKIP_PREFLIGHT=false. Setting both is a waste of rpc resources.

SEND_RPC_URLS

It is recommended to find multiple RPCs for sending, as they can land in different situations, and you want whoever is the fastest to land it for you. You can use the following tool to test how fast a RPC is to land a transaction to help you find the best ones:

https://github.com/Cetipoo/solana-rpc-benchmark-tool

USE_DIFFERENT_TX_FOR_SENDERS

By default, the bot will send the same transaction through all the senders. This can make sure the fastest land it and if it failed, you only lose one gas fee. When set USE_DIFFERENT_TX_FOR_SENDERS=true, the bot will use a different transaction for different senders. This will give you more profit if the oppotunity is big enough to allow more than one, but also will lose you more gas fee if all are failed.

COMPUTE_UNIT_PRICE

aka priority fee on solana. Default to 10001, which based on helius lab, is the minimum number that some validators will not drop your transaction.

In theory, the higher this is, the more likely your transaction can land and the faster it will land. But, this will result in higher gas fee as well. So you need to adjust this carefully and maybe in small incremental values to find the best value for your setup.

You can also use the tool to test different COMPUTE_UNIT_PRICE value to help you decide which one is better, do note that running the test will cost you gas fee for all the ones that landed:

https://github.com/Cetipoo/solana-rpc-benchmark-tool

Jito

Jito is a mev service on Solana, that allows you to send transaction with a tip, which will help you land faster, and it will ONLY land it if it succeeded. Which means no failed transaction will be landed! Hence no lose on gas fee! To learn more about jito, you can read here: https://www.jito.wtf/

ENABLE_JITO

When you set ENABLE_JITO=true, the bot will send the transaction through jito. This is basically a no brainer and should always be set. Jito does have a 50/s/ip rate limit, so if you are seeing too many opportunities that above this number, some may not go through with jito due to rate limit.

When using Jito, the transaction is not send through RPC, but send through Jito block engine. The bot will send the transaction to all 5 jito block engine, you want to be very close to at least one of them to minimize your sending time. https://jito-labs.gitbook.io/mev/searcher-resources/block-engine/mainnet-addresses

JITO_TIP_BP

Default to 5000, this means 50% of your profit will be used as the tip to Jito, which helps you to land the transaction. Jito runs an auction house on each Jito leader, and will order all the jito bundle by the tip they give. So in theory, if you and another person both try to seize the same opportunity, and you have a higher tip than the other person, you will be ranked higher and take the oppotunity and the other person will fail to get it. Though in reality there's a lot more to how Jito works, and higher tip does not always mean higher landing rate. This is something you should tweak and find the best number for your own setup.

Jupiter API

The bot provides a convenient way to run the Jupiter(V6) API direclty with the bot when setting the JUPITER_URL to http://0.0.0.0:18080. But you can also choose to host it yourself, or find a third party provider. As all the tuning we are doing here, our goal is to be fast. This tuning guide is mostly for the Jupiter API hosted together with the bot.

YELLOWSTONE_URL

Yellowstone(grpc) is a geyser plugin for Solana validator. You may see people talking about yellowstone/grpc/geyser, they are all the same thing. Read more here: https://docs.triton.one/project-yellowstone/introduction It provides a stream service that will push the data continually to the receiver. To run a high performance Jupiter API, this is something you must have. With this you may be able to run a Jupiter API with up to 1000 different mints. Without it, you will have a hard time to run even with 50 different mints.

To debug and see how your yellowstone performs, run debug.sh instead of run.sh, which will output some additional logs for jupiter. Pay attention to the logs like the following:

jupiter_core::geyser_client] Chunked geyser slots are lagging min: 265511878, max: 265511885

For the most performant yellowstone, you should barely see this log. You can see there's a lagging min and max number. If the difference is less than 10-20, it's usually still ok, if it's more then you may likely not getting any opportunity with it.

RPC_URL

Jupiter needs a normal rpc to read a few things. In addition, if you do not provide a YELLOWSTONE_URL, the bot will run Jupiter API in pull mode, which means it will make a huge amount of requests every few seconds. This can be a huge amount for some RPC, and you will see Jupiter crash or too many requests error if you set JUPITER_TOKEN_COUNT to be too high.

You should never set JUPITER_TOKEN_COUNT > 50 when you don't have YELLOWSTONE_URL. And 50 is for a very good rpc. Most rpc can only affort it to be 3-10

JUPITER_TOKEN_COUNT

When this is set to a none zero value, the bot will first pull the most popular mints from Birdeye, based on last 24h volume, then take top JUPITER_TOKEN_COUNT of them to use to run the Jupiter API.

As mentioned above, if you don't have YELLOWSTONE_URL, set this to 3 - 10 to start with. If you have a really good RPC, you can try 30-50, but never over 50. If you do have a YELLOWSTONE_URL, this number can go up to 300-1000. My testing shows it doesn't do much over 1000, but you can give it a try.

You will want to use your own Birdeye api when setting a number larger than 100. You can get it here: https://docs.birdeye.so/docs/birdeye-data-services-bds. Then set the API key in .env as BIRDEYE_API_KEY

Intermedium-mints.json

For an arb trade(A ->B ->A), B is called intermedium mint here. Instead of using tokens pulled from Birdeye, you can also add your own mints that you want to use as the intermedium mints. This can be helpful when you noticed a token that is has potential but don't have enough volume yet, or there's a big token launch coming and you just want to prepare for it.

If you don't want to use any mints pulled from Birdeye, you can do that by setting JUPITER_TOKEN_COUNT to 0, and add all the mints you want to use to intermedium-mints.json

Bot

The last step is tuning your bot. There's a lot of trade offs to make here, so you will likely need to test different combinations your self and find the best strategy that fits you.

USE_DIRECT_ROUTE_ONLY

When set to true, the bot will only look for 2hop(A -> B -> A) trade. Setting to true will lead to shorter quote time to Jupiter API, which end up with faster landing. When setting to false, the bot will also look for opportunities like (A -> B -> C -> A). Setting to false will lead to more opportunities, but taking more time to quote.

JUP_MAX_ACCOUNTS

This is used when quoting from Jupiter API. A solana transaction can contain at most 64 accounts. With the help of AddressLookupTable this number can go up.

The bot will quote twice for each mint, A ->B and B -> A. So the total accounts used will be 2 * JUP_MAX_ACCOUNTS in worst case.

For the bot specifically, you shouldn't try to go higher than 32 for the value. The higher the value is, the more complicated route it can found, but slower. With lower value, you will find less route but faster.

AUTO_RESTART

Bot can be set to automatically restart on a fixed time, this can be very helpful to catch the latest trend, when using with birdeye together. Though the restart itself may take a little time, so you will lose the oppotunities during the restart.

PROCESS_DELAY

Process delay means, for each mint + trade size, when the bot finished looking for arb for it, how long does the bot wait until it tries again. Setting a small number here, when you also have a large number of trade size + intermedium mints, can lead to a huge load to your RPC. If you tune this to a small number and stops seeing opportunities, this is what happening.

INTERMEDIUM_MINT_COUNT_FROM_BIRDEYE

This param configs how many top MINTS to use as intermedium mints. The mints are also pulled fro Birdeye. If you have too many intermedium tokens, it can slow down your jupiter quote because it's overloaded. So try a few different values and see which one works best for you.

Note: This should be equal or smaller than JUPITER_TOKEN_COUNT

base-mint.json

SolanaMevBot uses fixed trade size, with special optimization for it. When you set your trade size, you should try to make it spread over a large range, so it can get all possible opportunities.

e.g. If you have 100 SOL in your wallet, a good distribution can be like this:

1 SOL/2.5 SOL/5 SOL/10 SOL/25 SOL/50 SOL/100 SOL

Intermedium mints

Even though the bot will automatically fetch birdeye popular tokens, it's usually a bit too late to get the most profits. To maximize your chance of getting most profitable arbitrage trades, one way is to monitor new mints automaically or manually, and add them to your intermedium-mints.json. You can monitor through dexscreener.com/birdeye/geicoterminal etc. for the newest mints, or just follow a few top players like ben and bob to find them.

Multiple locations

If you already have a profitable setup, the easiest way to levelup is to duplicate it in different locations! Solana is a decentralized blockchain, a server in Asia may as a ping of >200ms to a server in U.S. By having more machines in different locations you are likely to get much more opportunities in time.

Note: If you want to use the same wallet for multiple locations, you should set USE_SEPARATE_TIP_ACCOUNT to true. Jito is rate limiting based on ip + wallet/region, this flag can get around this and let you be limited only by ip. You can also use different wallets for different locations.

Strategy

There are a lot of strategies you can use with the bot. Some of the examples are listed in Strategy. And that is just a list of examples.

Last updated