Ruka hadi 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 (inafunguka katika kichupo kipya), maktaba ya Web3 (inafunguka katika kichupo kipya), na Uniswap v3 (inafunguka katika kichupo kipya) kwa nukuu na biashara.

Kwa nini Python?

Lugha inayotumiwa sana kwa AI ni Python (inafunguka katika kichupo kipya), kwa hivyo tunaitumia hapa. Usijali ikiwa hujui Python. Lugha hii iko wazi sana, na nitaelezea kwa usahihi kile inachofanya.

Maktaba ya Web3 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya). 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 (inafunguka katika kichupo kipya))

  1. Ikiwa bado huna, pakua na usakinishe Python (inafunguka katika kichupo kipya).

  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 (inafunguka katika kichupo kipya). 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 (inafunguka katika kichupo kipya).

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 (inafunguka katika kichupo kipya). 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 (inafunguka katika kichupo kipya). Anwani hii tayari iko katika muundo wa checksum, lakini ni bora kutumia Web3.to_checksum_address (inafunguka katika kichupo kipya) ili kufanya msimbo uweze kutumika tena.

Hizi ni ABI (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) inatumika kuunganisha kwenye mkataba. Kumbuka (frozen=True). Katika Python booleans (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya), 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya). 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 (inafunguka katika kichupo kipya). Thamani tunayotaka hasa ni idadi ya dola kwa kila WETH, si kinyume chake.

Kipengele cha desimali ni uwiano kati ya vipengele vya desimali (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) mpya, tunatoa anwani na ABI kwa w3.eth.contract.

Kazi hii inarudisha kila kitu tunachohitaji kuhusu bwawa maalum (inafunguka katika kichupo kipya). Sintaksia f"<string>" ni kamba iliyoumbizwa (inafunguka katika kichupo kipya).

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 (inafunguka katika kichupo kipya).

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 (inafunguka katika kichupo kipya) kuiumbiza kwa muundo unaosomeka kwa binadamu na mifumo mikubwa ya lugha (LLMs). Tumia Decimal.quantize (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) kwa kawaida hupitia orodha. Orodha ya nambari za vitalu za kupata nukuu inatoka kwa range (inafunguka katika kichupo kipya).

        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 (inafunguka katika kichupo kipya) 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) (inafunguka katika kichupo kipya).

  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 (inafunguka katika kichupo kipya).

# 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 (inafunguka katika kichupo kipya), 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya)

  2. Weka pesa kwenye akaunti (inafunguka katika kichupo kipya)—kiasi cha chini wakati wa kuandika ni $5

  3. Unda ufunguo wa API (inafunguka katika kichupo kipya)

  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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya)

  2. Anza anvil (inafunguka katika kichupo kipya)

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

    anvil inasikiliza kwenye URL chaguo-msingi ya Foundry, http://localhost:8545 (inafunguka katika kichupo kipya), kwa hivyo hatuhitaji kubainisha URL kwa amri ya cast (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) kujenga muamala. Kisha tunatumia web3.eth.account.sign_transaction (inafunguka katika kichupo kipya) kutia saini muamala, kwa kutumia PRIVATE_KEY. Hatimaye, tunatumia w3.eth.send_raw_transaction (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya) au kwa kufunga mistari 368-400 katika kitanzi na kutumia time.sleep (inafunguka katika kichupo kipya) 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 (inafunguka katika kichupo kipya), 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 (inafunguka katika kichupo kipya). 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 (inafunguka katika kichupo kipya).

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 (inafunguka katika kichupo kipya).