Ruka hadi kwenye maudhui makuu

Tengeneza ajenti wako wa akili bandia wa kufanya biashara kwenye Ethereum

AI
biashara
ajenti
Python
Kati
Ori Pomerantz
13 Februari 2026
22 dakika za kusoma

Katika mafunzo haya utajifunza jinsi ya kujenga ajenti rahisi wa akili bandia wa kufanya biashara. Ajenti huyu anafanya kazi kwa kutumia hatua hizi:

  1. Kusoma bei za sasa na za zamani za tokeni, pamoja na taarifa nyingine zinazoweza kuwa muhimu
  2. Kujenga swali kwa kutumia taarifa hizi, pamoja na taarifa za msingi kuelezea jinsi zinavyoweza kuwa muhimu
  3. Kuwasilisha swali na kupokea makadirio ya bei
  4. Kufanya biashara kulingana na pendekezo
  5. Kusubiri na kurudia

Ajenti huyu anaonyesha jinsi ya kusoma taarifa, kuzitafsiri kuwa swali linalotoa jibu linaloweza kutumika, na kutumia jibu hilo. Hizi zote ni hatua zinazohitajika kwa ajenti wa akili bandia. Ajenti huyu ametekelezwa kwa Python kwa sababu ndiyo lugha inayotumiwa sana katika AI.

Kwa nini ufanye hivi?

Majenti wa biashara wa kujiendesha wanaruhusu wasanidi kuchagua na kutekeleza mkakati wa biashara. Majenti wa akili bandia wanaruhusu mikakati ya biashara iliyo ngumu zaidi na inayobadilika, ikiwezekana kutumia taarifa na aligoriti ambazo msanidi hata hajafikiria kuzitumia.

Zana

Mafunzo haya yanatumia Python (opens in a new tab), maktaba ya Web3 (opens in a new tab), na Uniswap v3 (opens in a new tab) kwa nukuu na biashara.

Kwa nini Python?

Lugha inayotumiwa sana kwa AI ni Python (opens in a new tab), kwa hivyo tunaitumia hapa. Usijali ikiwa hujui Python. Lugha hii iko wazi sana, na nitaelezea kwa usahihi kile inachofanya.

Maktaba ya Web3 (opens in a new tab) ndiyo API ya Ethereum ya Python inayojulikana zaidi. Ni rahisi sana kutumia.

Kufanya biashara kwenye mnyororo wa vitalu

Kuna mabadilishano mengi yaliyosambazwa (DEX) yanayokuruhusu kufanya biashara ya tokeni kwenye Ethereum. Hata hivyo, huwa na viwango vya ubadilishaji vinavyofanana kutokana na arbitrage.

Uniswap (opens in a new tab) ni DEX inayotumiwa sana ambayo tunaweza kuitumia kwa nukuu (kuona thamani za tokeni zinazolingana) na biashara.

OpenAI

Kwa mfumo mkubwa wa lugha, nilichagua kuanza na OpenAI (opens in a new tab). Ili kuendesha programu katika mafunzo haya utahitaji kulipia ufikiaji wa API. Malipo ya chini ya $5 yanatosha kabisa.

Uendelezaji, hatua kwa hatua

Ili kurahisisha uendelezaji, tunaendelea kwa hatua. Kila hatua ni tawi katika GitHub.

Kuanza

Kuna hatua za kuanza chini ya UNIX au Linux (ikiwa ni pamoja na WSL (opens in a new tab))

  1. Ikiwa bado huna, pakua na usakinishe Python (opens in a new tab).

  2. Nakili hazina ya GitHub.

    git clone https://github.com/qbzzt/260215-ai-agent.git -b 01-getting-started
    cd 260215-ai-agent
    
  3. Sakinisha uv (opens in a new tab). Amri kwenye mfumo wako inaweza kuwa tofauti.

    pipx install uv
    
  4. Pakua maktaba.

    uv sync
    
  5. Washa mazingira pepe.

    source .venv/bin/activate
    
  6. Ili kuthibitisha kuwa Python na Web3 zinafanya kazi kwa usahihi, endesha python3 na uipe programu hii. Unaweza kuiingiza kwenye kidokezo cha >>>; hakuna haja ya kuunda faili.

    from web3 import Web3
    MAINNET_URL = "https://eth.drpc.org"
    w3 = Web3(Web3.HTTPProvider(MAINNET_URL))
    w3.eth.block_number
    quit()
    

Kusoma kutoka kwenye mnyororo wa vitalu

Hatua inayofuata ni kusoma kutoka kwenye mnyororo wa vitalu. Ili kufanya hivyo, unahitaji kubadilisha kwenda kwenye tawi la 02-read-quote na kisha utumie uv kuendesha programu.

git checkout 02-read-quote
uv run agent.py

Unapaswa kupokea orodha ya vitu vya Quote, kila kimoja kikiwa na muhuri wa muda, bei, na rasilimali (kwa sasa kila wakati ni WETH/USDC).

Hapa kuna maelezo ya mstari kwa mstari.

Ingiza maktaba tunazohitaji. Zimeelezwa hapa chini zinapotumika.

print = functools.partial(print, flush=True)

Inabadilisha print ya Python na toleo ambalo kila wakati hutoa matokeo mara moja. Hii ni muhimu katika hati inayoendeshwa kwa muda mrefu kwa sababu hatutaki kusubiri sasisho za hali au matokeo ya utatuzi.

MAINNET_URL = "https://eth.drpc.org"

URL ya kufika kwenye Mtandao Mkuu. Unaweza kupata moja kutoka kwa Nodi kama huduma au kutumia mojawapo ya zile zilizotangazwa katika Chainlist (opens in a new tab).

BLOCK_TIME_SECONDS = 12
MINUTE_BLOCKS = int(60 / BLOCK_TIME_SECONDS)
HOUR_BLOCKS = MINUTE_BLOCKS * 60
DAY_BLOCKS = HOUR_BLOCKS * 24

Kitalu cha Mtandao Mkuu wa Ethereum kwa kawaida hutokea kila sekunde kumi na mbili, kwa hivyo hizi ni idadi ya vitalu tunavyotarajia kutokea katika kipindi cha muda. Kumbuka kuwa hii si takwimu kamili. Wakati mpendekezaji wa bloku yuko chini, kitalu hicho hurukwa, na muda wa kitalu kinachofuata ni sekunde 24. Ikiwa tungetaka kupata kitalu kamili kwa muhuri wa muda, tungetumia utafutaji wa binary (opens in a new tab). Hata hivyo, hii inakaribia kutosha kwa madhumuni yetu. Kutabiri siku zijazo si sayansi kamili.

CYCLE_BLOCKS = DAY_BLOCKS

Ukubwa wa mzunguko. Tunakagua nukuu mara moja kwa kila mzunguko na kujaribu kukadiria thamani mwishoni mwa mzunguko unaofuata.

# Anwani ya bwawa tunalosoma
WETHUSDC_ADDRESS = Web3.to_checksum_address("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")

Thamani za nukuu zinachukuliwa kutoka kwenye bwawa la Uniswap 3 USDC/WETH kwenye anwani 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 (opens in a new tab). Anwani hii tayari iko katika muundo wa checksum, lakini ni bora kutumia Web3.to_checksum_address (opens in a new tab) ili kufanya msimbo uweze kutumika tena.

Hizi ni ABI (opens in a new tab) za mikataba miwili tunayohitaji kuwasiliana nayo. Ili kuweka msimbo kwa ufupi, tunajumuisha tu kazi tunazohitaji kuita.

w3 = Web3(Web3.HTTPProvider(MAINNET_URL))

Anzisha maktaba ya Web3 (opens in a new tab) na uunganishe kwenye nodi ya Ethereum.

@dataclass(frozen=True)
class ERC20Token:
    address: str
    symbol: str
    decimals: int
    contract: Contract

Hii ni njia moja ya kuunda darasa la data katika Python. Aina ya data ya Contract (opens in a new tab) inatumika kuunganisha kwenye mkataba. Kumbuka (frozen=True). Katika Python booleans (opens in a new tab) zinafafanuliwa kama True au False, kwa herufi kubwa. Darasa hili la data ni frozen, ikimaanisha sehemu haziwezi kurekebishwa.

Kumbuka uingizaji (indentation). Tofauti na lugha zinazotokana na C (opens in a new tab), Python inatumia uingizaji kuashiria vitalu. Mkalimani wa Python anajua kuwa ufafanuzi ufuatao si sehemu ya darasa hili la data kwa sababu hauanzi kwenye uingizaji sawa na sehemu za darasa la data.

@dataclass(frozen=True)
class PoolInfo:
    address: str
    token0: ERC20Token
    token1: ERC20Token
    contract: Contract
    asset: str
    decimal_factor: Decimal = 1

Aina ya Decimal (opens in a new tab) inatumika kwa kushughulikia kwa usahihi sehemu za desimali.

    def get_price(self, block: int) -> Decimal:

Hii ndiyo njia ya kufafanua kazi katika Python. Ufafanuzi umeingizwa ili kuonyesha bado ni sehemu ya PoolInfo.

Katika kazi ambayo ni sehemu ya darasa la data kigezo cha kwanza kila wakati ni self, mfano wa darasa la data ulioita hapa. Hapa kuna kigezo kingine, nambari ya kitalu.

        assert block <= w3.eth.block_number, "Block is in the future"

Kama tungeweza kusoma siku zijazo, tusingehitaji AI kwa biashara.

        sqrt_price_x96 = Decimal(self.contract.functions.slot0().call(block_identifier=block)[0])

Sintaksia ya kuita kazi kwenye EVM kutoka Web3 ni hii: <contract object>.functions.<function name>().call(<parameters>). Vigezo vinaweza kuwa vigezo vya kazi ya EVM (kama vipo; hapa hakuna) au vigezo vilivyotajwa (opens in a new tab) kwa kurekebisha tabia ya mnyororo wa vitalu. Hapa tunatumia kimoja, block_identifier, kubainisha nambari ya kitalu tunayotaka kuendesha.

Matokeo ni muundo huu, katika umbo la safu (opens in a new tab). Thamani ya kwanza ni kazi ya kiwango cha ubadilishaji kati ya tokeni mbili.

        raw_price = (sqrt_price_x96 / Decimal(2**96)) ** 2

Ili kupunguza hesabu mnyororoni, Uniswap v3 haihifadhi kipengele halisi cha ubadilishaji bali kipeo chake cha pili. Kwa sababu EVM haiauni hesabu za nambari zinazoelea au sehemu, badala ya thamani halisi, jibu ni price296

         # (tokeni1 kwa kila tokeni0)
        return 1/(raw_price * self.decimal_factor)

Bei ghafi tunayopata ni idadi ya token0 tunayopata kwa kila token1. Katika bwawa letu token0 ni USDC (sarafu thabiti yenye thamani sawa na dola ya Marekani) na token1 ni WETH (opens in a new tab). Thamani tunayotaka hasa ni idadi ya dola kwa kila WETH, si kinyume chake.

Kipengele cha desimali ni uwiano kati ya vipengele vya desimali (opens in a new tab) kwa tokeni mbili.

@dataclass(frozen=True)
class Quote:
    timestamp: str
    price: Decimal
    asset: str

Darasa hili la data linawakilisha nukuu: bei ya rasilimali maalum kwa wakati fulani. Katika hatua hii, sehemu ya asset haina umuhimu kwa sababu tunatumia bwawa moja na kwa hivyo tuna rasilimali moja. Hata hivyo, tutaongeza rasilimali zaidi baadaye.

Kazi hii inachukua anwani na kurudisha taarifa kuhusu mkataba wa tokeni kwenye anwani hiyo. Ili kuunda Web3 Contract (opens in a new tab) mpya, tunatoa anwani na ABI kwa w3.eth.contract.

Kazi hii inarudisha kila kitu tunachohitaji kuhusu bwawa maalum (opens in a new tab). Sintaksia f"<string>" ni kamba iliyoumbizwa (opens in a new tab).

def get_quote(pool: PoolInfo, block_number: int = None) -> Quote:

Pata kitu cha Quote. Thamani chaguo-msingi ya block_number ni None (hakuna thamani).

    if block_number is None:
        block_number = w3.eth.block_number

Ikiwa nambari ya kitalu haikubainishwa, tumia w3.eth.block_number, ambayo ni nambari ya kitalu cha hivi punde. Hii ni sintaksia ya taarifa ya if (opens in a new tab).

Inaweza kuonekana kana kwamba ingekuwa bora kuweka tu chaguo-msingi kuwa w3.eth.block_number, lakini hiyo haifanyi kazi vizuri kwa sababu itakuwa nambari ya kitalu wakati kazi inafafanuliwa. Katika ajenti anayeendeshwa kwa muda mrefu, hili lingekuwa tatizo.

    block = w3.eth.get_block(block_number)
    price = pool.get_price(block_number)
    return Quote(
        timestamp=datetime.fromtimestamp(block.timestamp, timezone.utc).isoformat(),
        price=price.quantize(Decimal("0.01")),
        asset=pool.asset
    )

Tumia maktaba ya datetime (opens in a new tab) kuiumbiza kwa muundo unaosomeka kwa binadamu na mifumo mikubwa ya lugha (LLMs). Tumia Decimal.quantize (opens in a new tab) kuzungusha thamani kwa nafasi mbili za desimali.

def get_quotes(pool: PoolInfo, start_block: int, end_block: int, step: int) -> list[Quote]:

Katika Python unafafanua orodha (opens in a new tab) inayoweza tu kuwa na aina maalum kwa kutumia list[<type>].

    quotes = []
    for block in range(start_block, end_block + 1, step):

Katika Python kitanzi cha for (opens in a new tab) kwa kawaida hupitia orodha. Orodha ya nambari za vitalu za kupata nukuu inatoka kwa range (opens in a new tab).

        quote = get_quote(pool, block)
        quotes.append(quote)
    return quotes

Kwa kila nambari ya kitalu, pata kitu cha Quote na ukiambatanishe kwenye orodha ya quotes. Kisha rudisha orodha hiyo.

Huu ndio msimbo mkuu wa hati. Soma taarifa za bwawa, pata nukuu kumi na mbili, na pprint (opens in a new tab) hizo.

Kuunda kidokezo

Kisha, tunahitaji kubadilisha orodha hii ya nukuu kuwa kidokezo kwa LLM na kupata thamani inayotarajiwa ya baadaye.

git checkout 03-create-prompt
uv run agent.py

Matokeo sasa yatakuwa kidokezo kwa LLM, sawa na:

Kumbuka kuwa kuna nukuu za rasilimali mbili hapa, WETH/USDC na WBTC/WETH. Kuongeza nukuu kutoka kwa rasilimali nyingine kunaweza kuboresha usahihi wa utabiri.

Jinsi kidokezo kinavyoonekana

Kidokezo hiki kina sehemu tatu, ambazo ni za kawaida sana katika vidokezo vya LLM.

  1. Taarifa. LLM zina taarifa nyingi kutoka kwenye mafunzo yao, lakini kwa kawaida hazina za hivi punde. Hii ndiyo sababu tunahitaji kupata nukuu za hivi punde hapa. Kuongeza taarifa kwenye kidokezo kunaitwa retrieval augmented generation (RAG) (opens in a new tab).

  2. Swali halisi. Hiki ndicho tunachotaka kujua.

  3. Maagizo ya uumbizaji wa matokeo. Kwa kawaida, LLM itatupa makadirio na maelezo ya jinsi ilivyofikia. Hii ni bora kwa binadamu, lakini programu ya kompyuta inahitaji tu jibu la mwisho.

Maelezo ya msimbo

Hapa kuna msimbo mpya.

from datetime import datetime, timezone, timedelta

Tunahitaji kuipa LLM muda ambao tunataka makadirio. Ili kupata muda "dakika/saa/siku n" katika siku zijazo, tunatumia darasa la timedelta (opens in a new tab).

# Anwani za mabwawa tunayosoma
WETHUSDC_ADDRESS = Web3.to_checksum_address("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
WETHWBTC_ADDRESS = Web3.to_checksum_address("0xCBCdF9626bC03E24f779434178A73a0B4bad62eD")

Tuna mabwawa mawili tunayohitaji kusoma.

Katika bwawa la WETH/USDC, tunataka kujua ni token0 (USDC) ngapi tunahitaji kununua moja ya token1 (WETH). Katika bwawa la WETH/WBTC, tunataka kujua ni token1 (WETH) ngapi tunahitaji kununua moja ya token0 (WBTC, ambayo ni Bitcoin iliyofungwa). Tunahitaji kufuatilia ikiwa uwiano wa bwawa unahitaji kugeuzwa.

Ili kujua ikiwa bwawa linahitaji kugeuzwa, tunapata hiyo kama ingizo kwa read_pool. Pia, alama ya rasilimali inahitaji kuwekwa kwa usahihi.

Sintaksia <a> if <b> else <c> ni sawa na Python ya kigezo cha masharti cha ternary (opens in a new tab), ambayo katika lugha inayotokana na C ingekuwa <b> ? <a> : <c>.

def format_quotes(quotes: list[Quote]) -> str:
    result = f"Asset: {quotes[0].asset}\n"
    for quote in quotes:
        result += f"\t{quote.timestamp[0:16]} {quote.price.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)}\n"
    return result

Kazi hii inajenga kamba inayoumbiza orodha ya vitu vya Quote, ikichukulia vyote vinatumika kwa rasilimali sawa.

def make_prompt(quotes: list[list[Quote]], expected_time: str, asset: str) -> str:
    return f"""

Katika Python kamba halisi za mistari mingi (opens in a new tab) huandikwa kama """ .... """.

Given these quotes:
{
    functools.reduce(lambda acc, q: acc + '\n' + q,
        map(lambda q: format_quotes(q), quotes))
}

Hapa, tunatumia muundo wa MapReduce (opens in a new tab) kuzalisha kamba kwa kila orodha ya nukuu na format_quotes, kisha kuzipunguza kuwa kamba moja kwa matumizi katika kidokezo.

What would you expect the value for {asset} to be at time {expected_time}?

Provide your answer as a single number rounded to two decimal places,
without any other text.
    """

Sehemu iliyobaki ya kidokezo ni kama inavyotarajiwa.

Kagua mabwawa mawili na upate nukuu kutoka kwa yote mawili.

future_time = (datetime.now(timezone.utc) + timedelta(days=1)).isoformat()[0:16]

print(make_prompt(wethusdc_quotes + wethwbtc_quotes, future_time, wethusdc_pool.asset))

Amua hatua ya muda ujao ambayo tunataka makadirio, na uunde kidokezo.

Kuunganisha na LLM

Kisha, tunadokeza LLM halisi na kupokea thamani inayotarajiwa ya baadaye. Niliandika programu hii kwa kutumia OpenAI, kwa hivyo ikiwa unataka kutumia mtoa huduma tofauti, utahitaji kuirekebisha.

  1. Pata akaunti ya OpenAI (opens in a new tab)

  2. Weka pesa kwenye akaunti (opens in a new tab)—kiasi cha chini wakati wa kuandika ni $5

  3. Unda ufunguo wa API (opens in a new tab)

  4. Katika mstari wa amri, hamisha ufunguo wa API ili programu yako iweze kuutumia

    export OPENAI_API_KEY=sk-<the rest of the key goes here>
    
  5. Angalia na uendeshe ajenti

    git checkout 04-interface-llm
    uv run agent.py
    

Hapa kuna msimbo mpya.

from openai import OpenAI

open_ai = OpenAI()  # Mteja anasoma kibadilika cha mazingira cha OPENAI_API_KEY

Ingiza na uanzishe API ya OpenAI.

response = open_ai.chat.completions.create(
    model="gpt-4-turbo",
    messages=[
        {"role": "user", "content": prompt}
    ],
    temperature=0.0,
    max_tokens=16,
)

Ita API ya OpenAI (open_ai.chat.completions.create) ili kuunda jibu.

Toa bei na utoe pendekezo la kununua au kuuza.

Kujaribu utabiri

Sasa kwa kuwa tunaweza kuzalisha utabiri, tunaweza pia kutumia data ya kihistoria kutathmini ikiwa tunazalisha utabiri muhimu.

uv run test-predictor.py

Matokeo yanayotarajiwa ni sawa na:

Sehemu kubwa ya kijaribu inafanana na ajenti, lakini hapa kuna sehemu ambazo ni mpya au zilizorekebishwa.

Tunaangalia siku CYCLES_FOR_TEST (zilizobainishwa kama 40 hapa) nyuma.

# Tengeneza utabiri na uukague dhidi ya historia halisi

total_error = Decimal(0)
changes = []

Kuna aina mbili za makosa tunayovutiwa nayo. Ya kwanza, total_error, ni jumla ya makosa yaliyofanywa na mtabiri.

Ili kuelewa ya pili, changes, tunahitaji kukumbuka madhumuni ya ajenti. Sio kutabiri uwiano wa WETH/USDC (bei ya ETH). Ni kutoa mapendekezo ya kuuza na kununua. Ikiwa bei kwa sasa ni $2000 na inatabiri $2010 kesho, hatujali ikiwa matokeo halisi ni $2020 na tunapata pesa za ziada. Lakini tunajali ikiwa ilitabiri $2010, na kununua ETH kulingana na pendekezo hilo, na bei inashuka hadi $1990.

for index in range(0,len(wethusdc_quotes)-CYCLES_BACK):

Tunaweza tu kuangalia kesi ambapo historia kamili (thamani zilizotumika kwa utabiri na thamani ya ulimwengu halisi ya kulinganisha nayo) inapatikana. Hii inamaanisha kesi mpya zaidi lazima iwe ile iliyoanza CYCLES_BACK iliyopita.

    wethusdc_slice = wethusdc_quotes[index:index+CYCLES_BACK]
    wethwbtc_slice = wethwbtc_quotes[index:index+CYCLES_BACK]

Tumia vipande (opens in a new tab) kupata idadi sawa ya sampuli kama nambari anayotumia ajenti. Msimbo kati ya hapa na sehemu inayofuata ni msimbo ule ule wa kupata-utabiri tulio nao katika ajenti.

    predicted_price = Decimal(response.choices[0].message.content.strip())
    real_price = wethusdc_quotes[index+CYCLES_BACK].price
    prediction_time_price = wethusdc_quotes[index+CYCLES_BACK-1].price

Pata bei iliyotabiriwa, bei halisi, na bei wakati wa utabiri. Tunahitaji bei wakati wa utabiri ili kuamua ikiwa pendekezo lilikuwa la kununua au kuuza.

    error = abs(predicted_price - real_price)
    total_error += error
    print (f"Prediction for {prediction_time}: predicted {predicted_price} USD, real {real_price} USD, error {error} USD")

Tafuta kosa, na uongeze kwenye jumla.

    recomended_action = 'buy' if predicted_price > prediction_time_price else 'sell'
    price_increase = real_price - prediction_time_price
    changes.append(price_increase if recomended_action == 'buy' else -price_increase)

Kwa changes, tunataka athari ya kifedha ya kununua au kuuza ETH moja. Kwa hivyo kwanza, tunahitaji kuamua pendekezo, kisha kutathmini jinsi bei halisi ilivyobadilika, na ikiwa pendekezo lilitengeneza pesa (mabadiliko chanya) au kugharimu pesa (mabadiliko hasi).

print (f"Mean prediction error over {len(wethusdc_quotes)-CYCLES_BACK} predictions: {total_error / Decimal(len(wethusdc_quotes)-CYCLES_BACK)} USD")

length_changes = Decimal(len(changes))
mean_change = sum(changes, Decimal(0)) / length_changes
print (f"Mean change per recommendation: {mean_change} USD")
var = sum((x - mean_change) ** 2 for x in changes) / length_changes
print (f"Standard variance of changes: {var.sqrt().quantize(Decimal("0.01"))} USD")

Ripoti matokeo.

print (f"Profitable days: {len(list(filter(lambda x: x > 0, changes)))/length_changes:.2%}")
print (f"Losing days: {len(list(filter(lambda x: x < 0, changes)))/length_changes:.2%}")

Tumia filter (opens in a new tab) kuhesabu idadi ya siku zenye faida na idadi ya siku za gharama. Matokeo ni kitu cha kichujio, ambacho tunahitaji kubadilisha kuwa orodha ili kupata urefu.

Kuwasilisha miamala

Sasa tunahitaji kuwasilisha miamala. Hata hivyo, sitaki kutumia pesa halisi katika hatua hii, kabla ya mfumo kuthibitishwa. Badala yake, tutaunda mchepuo wa ndani wa Mtandao Mkuu, na "kufanya biashara" kwenye mtandao huo.

Hapa kuna hatua za kuunda mchepuo wa ndani na kuwezesha biashara.

  1. Sakinisha Foundry (opens in a new tab)

  2. Anza anvil (opens in a new tab)

    anvil --fork-url https://eth.drpc.org --block-time 12
    

    anvil inasikiliza kwenye URL chaguo-msingi ya Foundry, http://localhost:8545 (opens in a new tab), kwa hivyo hatuhitaji kubainisha URL kwa amri ya cast (opens in a new tab) tunayotumia kudhibiti mnyororo wa vitalu.

  3. Wakati wa kuendesha katika anvil, kuna akaunti kumi za majaribio ambazo zina ETH—weka vigezo vya mazingira kwa ya kwanza

    PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
    ADDRESS=`cast wallet address $PRIVATE_KEY`
    
  4. Hii ndiyo mikataba tunayohitaji kutumia. SwapRouter (opens in a new tab) ni mkataba wa Uniswap v3 tunaotumia kufanya biashara hasa. Tungeweza kufanya biashara moja kwa moja kupitia bwawa, lakini hii ni rahisi zaidi.

    Vigezo viwili vya chini ni njia za Uniswap v3 zinazohitajika kwa badilishano kati ya WETH na USDC.

    WETH_ADDRESS=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    USDC_ADDRESS=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    POOL_ADDRESS=0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640
    SWAP_ROUTER=0xE592427A0AEce92De3Edee1F18E0157C05861564
    WETH_TO_USDC=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc20001F4A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    USDC_TO_WETH=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480001F4C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    
  5. Kila moja ya akaunti za majaribio ina 10,000 ETH. Tumia mkataba wa WETH kufunga 1000 ETH ili kupata 1000 WETH kwa biashara.

    cast send $WETH_ADDRESS "deposit()" --value 1000ether --private-key $PRIVATE_KEY
    
  6. Tumia SwapRouter kufanya biashara ya 500 WETH kwa USDC.

    cast send $WETH_ADDRESS "approve(address,uint256)" $SWAP_ROUTER 500ether --private-key $PRIVATE_KEY
    MAXINT=`cast max-int uint256`
    cast send $SWAP_ROUTER \
        "exactInput((bytes,address,uint256,uint256,uint256))" \
        "($WETH_TO_USDC,$ADDRESS,$MAXINT,500ether,1000000)" \
        --private-key $PRIVATE_KEY
    

    Wito wa approve unaunda kibali kinachoruhusu SwapRouter kutumia baadhi ya tokeni zetu. Mikataba haiwezi kufuatilia matukio, kwa hivyo ikiwa tutafanya hamisho la tokeni moja kwa moja kwenye mkataba wa SwapRouter, isingejua ililipwa. Badala yake, tunaruhusu mkataba wa SwapRouter kutumia kiasi fulani, na kisha SwapRouter inafanya hivyo. Hii inafanywa kupitia kazi inayoitwa na SwapRouter, kwa hivyo inajua ikiwa ilifanikiwa.

  7. Thibitisha una kiasi cha kutosha cha tokeni zote mbili.

    cast call $WETH_ADDRESS "balanceOf(address)" $ADDRESS | cast from-wei
    echo `cast call $USDC_ADDRESS "balanceOf(address)" $ADDRESS | cast to-dec`/10^6 | bc
    

Sasa kwa kuwa tuna WETH na USDC, tunaweza kuendesha ajenti.

git checkout 05-trade
uv run agent.py

Matokeo yataonekana sawa na:

Ili kuitumia hasa, unahitaji mabadiliko machache madogo.

  • Katika mstari wa 14, badilisha MAINNET_URL kuwa kituo halisi cha ufikiaji, kama vile https://eth.drpc.org
  • Katika mstari wa 28, badilisha PRIVATE_KEY kuwa ufunguo wa siri wako mwenyewe
  • Isipokuwa wewe ni tajiri sana na unaweza kununua au kuuza 1 ETH kila siku kwa ajenti ambaye hajathibitishwa, unaweza kutaka kubadilisha 29 ili kupunguza WETH_TRADE_AMOUNT

Maelezo ya msimbo

Hapa kuna msimbo mpya.

SWAP_ROUTER_ADDRESS=Web3.to_checksum_address("0xE592427A0AEce92De3Edee1F18E0157C05861564")
WETH_TO_USDC=bytes.fromhex("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc20001F4A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
USDC_TO_WETH=bytes.fromhex("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480001F4C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

Vigezo vile vile tulivyotumia katika hatua ya 4.

WETH_TRADE_AMOUNT=1

Kiasi cha kufanyia biashara.

ERC20_ABI = [
    { "name": "symbol", ... },
    { "name": "decimals", ... },
    { "name": "balanceOf", ...},
    { "name": "approve", ...}
]

Ili kufanya biashara hasa, tunahitaji kazi ya approve. Pia tunataka kuonyesha salio kabla na baada, kwa hivyo tunahitaji pia balanceOf.

SWAP_ROUTER_ABI = [
  { "name": "exactInput", ...},
]

Katika ABI ya SwapRouter tunahitaji tu exactInput. Kuna kazi inayohusiana, exactOutput, ambayo tungeweza kutumia kununua WETH moja haswa, lakini kwa urahisi tunatumia tu exactInput katika visa vyote viwili.

account = w3.eth.account.from_key(PRIVATE_KEY)
swap_router = w3.eth.contract(
    address=SWAP_ROUTER_ADDRESS,
    abi=SWAP_ROUTER_ABI
)

Ufafanuzi wa Web3 kwa account (opens in a new tab) na mkataba wa SwapRouter.

def txn_params() -> dict:
    return {
        "from": account.address,
        "value": 0,
        "gas": 300000,
        "nonce": w3.eth.get_transaction_count(account.address),
    }

Vigezo vya muamala. Tunahitaji kazi hapa kwa sababu nonsi (opens in a new tab) lazima ibadilike kila wakati.

def approve_token(contract: Contract, amount: int):

Idhinisha kibali cha tokeni kwa SwapRouter.

    txn = contract.functions.approve(SWAP_ROUTER_ADDRESS, amount).build_transaction(txn_params())
    signed_txn = w3.eth.account.sign_transaction(txn, private_key=PRIVATE_KEY)
    tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)

Hivi ndivyo tunavyotuma muamala katika Web3. Kwanza tunatumia kitu cha Contract (opens in a new tab) kujenga muamala. Kisha tunatumia web3.eth.account.sign_transaction (opens in a new tab) kutia saini muamala, kwa kutumia PRIVATE_KEY. Hatimaye, tunatumia w3.eth.send_raw_transaction (opens in a new tab) kutuma muamala.

    print(f"Approve transaction sent: {tx_hash.hex()}")
    w3.eth.wait_for_transaction_receipt(tx_hash)
    print("Approve transaction mined.")

w3.eth.wait_for_transaction_receipt (opens in a new tab) inasubiri hadi muamala uchimbwe. Inarudisha stakabadhi ikihitajika.

SELL_PARAMS = {
    "path": WETH_TO_USDC,
    "recipient": account.address,
    "deadline": 2**256 - 1,
    "amountIn": WETH_TRADE_AMOUNT * 10 ** wethusdc_pool.token1.decimals,
    "amountOutMinimum": 0,
}

Hivi ni vigezo wakati wa kuuza WETH.

def make_buy_params(quote: Quote) -> dict:
    return {
        "path": USDC_TO_WETH,
        "recipient": account.address,
        "deadline": 2**256 - 1,
        "amountIn": int(quote.price*WETH_TRADE_AMOUNT) * 10**wethusdc_pool.token0.decimals,
        "amountOutMinimum": 0,
    }

Tofauti na SELL_PARAMS, vigezo vya kununua vinaweza kubadilika. Kiasi cha kuingiza ni gharama ya 1 WETH, kama inavyopatikana katika quote.

Kazi za buy() na sell() zinakaribia kufanana. Kwanza tunaidhinisha kibali cha kutosha kwa SwapRouter, na kisha tunaiita kwa njia na kiasi sahihi.

def balances():
    token0_balance = wethusdc_pool.token0.contract.functions.balanceOf(account.address).call()
    token1_balance = wethusdc_pool.token1.contract.functions.balanceOf(account.address).call()

    print(f"{wethusdc_pool.token0.symbol} Balance: {Decimal(token0_balance) / Decimal(10 ** wethusdc_pool.token0.decimals)}")
    print(f"{wethusdc_pool.token1.symbol} Balance: {Decimal(token1_balance) / Decimal(10 ** wethusdc_pool.token1.decimals)}")

Ripoti salio la mtumiaji katika sarafu zote mbili.

Ajenti huyu kwa sasa anafanya kazi mara moja tu. Hata hivyo, unaweza kumbadilisha ili afanye kazi mfululizo ama kwa kumuendesha kutoka crontab (opens in a new tab) au kwa kufunga mistari 368-400 katika kitanzi na kutumia time.sleep (opens in a new tab) kusubiri hadi wakati wa mzunguko unaofuata.

Maboresho yanayowezekana

Hili si toleo kamili la uzalishaji; ni mfano tu wa kufundisha mambo ya msingi. Hapa kuna baadhi ya mawazo ya maboresho.

Biashara nadhifu zaidi

Kuna ukweli mbili muhimu ambazo ajenti anapuuza anapoamua nini cha kufanya.

  • Ukubwa wa mabadiliko yanayotarajiwa. Ajenti anauza kiasi maalum cha WETH ikiwa bei inatarajiwa kushuka, bila kujali ukubwa wa kushuka huko. Inawezekana, ingekuwa bora kupuuza mabadiliko madogo na kuuza kulingana na kiasi gani tunatarajia bei kushuka.
  • Kwingineko ya sasa. Ikiwa 10% ya kwingineko yako iko katika WETH na unafikiri bei itapanda, labda ina maana kununua zaidi. Lakini ikiwa 90% ya kwingineko yako iko katika WETH, unaweza kuwa umejiweka wazi vya kutosha, na hakuna haja ya kununua zaidi. Kinyume chake ni kweli ikiwa unatarajia bei kushuka.

Vipi ikiwa unataka kuweka mkakati wako wa biashara siri?

Wachuuzi wa AI wanaweza kuona maswali unayotuma kwa LLM zao, ambayo yanaweza kufichua mfumo wa biashara wa kipekee uliouendeleza na ajenti wako. Mfumo wa biashara ambao watu wengi sana wanautumia hauna thamani kwa sababu watu wengi sana wanajaribu kununua wakati unataka kununua (na bei inapanda) na kujaribu kuuza wakati unataka kuuza (na bei inashuka).

Unaweza kuendesha LLM ndani ya nchi, kwa mfano, kwa kutumia LM-Studio (opens in a new tab), ili kuepuka tatizo hili.

Kutoka kwa boti ya AI hadi ajenti wa akili bandia

Unaweza kutoa hoja nzuri kwamba hii ni boti ya AI, sio ajenti wa akili bandia. Inatekeleza mkakati rahisi kiasi ambao unategemea taarifa zilizofafanuliwa awali. Tunaweza kuwezesha kujiboresha, kwa mfano, kwa kutoa orodha ya mabwawa ya Uniswap v3 na thamani zao za hivi punde na kuuliza ni mchanganyiko upi una thamani bora ya utabiri.

Ulinzi wa tofauti ya utekelezaji

Kwa sasa hakuna ulinzi wa tofauti ya utekelezaji (opens in a new tab). Ikiwa nukuu ya sasa ni $2000, na bei inayotarajiwa ni $2100, ajenti atanunua. Hata hivyo, ikiwa kabla ya ajenti kununua gharama inapanda hadi $2200, haina maana kununua tena.

Ili kutekeleza ulinzi wa tofauti ya utekelezaji, bainisha thamani ya amountOutMinimum katika mistari ya 325 na 334 ya agent.py (opens in a new tab).

Hitimisho

Tunatumai, sasa unajua vya kutosha kuanza na majenti wa akili bandia. Huu sio muhtasari wa kina wa somo; kuna vitabu vizima vilivyotengwa kwa hilo, lakini hii inatosha kukuanzisha. Kila la heri!

Tazama hapa kwa kazi zangu zaidi (opens in a new tab).