Ruka kwenda kwenye maudhui makuu

Tengeneza wakala wako wa biashara wa AI kwenye Ethereum

AI
biashara
wakala
python
Intermediate
Ori Pomerantz
13 Februari 2026
22 minute read

Katika mafunzo haya utajifunza jinsi ya kujenga wakala rahisi wa biashara wa AI. Wakala huyu hufanya kazi kwa kutumia hatua hizi:

  1. Soma bei za sasa na za zamani za tokeni, pamoja na taarifa nyingine zinazoweza kuwa muhimu
  2. Unda swali kwa taarifa hii, pamoja na taarifa ya usuli ili kueleza jinsi inavyoweza kuwa muhimu
  3. Wasilisha swali na upokee bei iliyokadiriwa
  4. Fanya biashara kulingana na pendekezo
  5. Subiri na urudie

Wakala huyu anaonyesha jinsi ya kusoma taarifa, kuitafsiri kuwa swali linalotoa jibu linaloweza kutumika, na kutumia jibu hilo. Hizi zote ni hatua zinazohitajika kwa wakala wa AI. Wakala huyu ametekelezwa katika Python kwa sababu ndiyo lugha ya kawaida inayotumika katika AI.

Kwa nini ufanye hivi?

Wakala wa biashara wa kiotomatiki huruhusu wasanidi programu kuchagua na kutekeleza mkakati wa biashara. Wakala wa AI huruhusu mikakati changamano na yenye nguvu zaidi ya biashara, inayoweza kutumia taarifa na kanuni ambazo msanidi programu hata hajafikiria kutumia.

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 inayotumika sana kwa AI ni Python (opens in a new tab), kwa hivyo tunaitumia hapa. Usijali kama hujui Python. Lugha iko wazi sana, na nitaeleza hasa inachofanya.

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

Kufanya biashara kwenye mnyororo wa bloku

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

Uniswap (opens in a new tab) ni DEX inayotumika sana ambayo tunaweza kutumia kwa nukuu (kuona thamani za tokeni linganishi) na biashara.

OpenAI

Kwa muundo mkuu 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 ni zaidi ya ya kutosha.

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 (pamoja na WSL (opens in a new tab))

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

  2. Clone hazina ya GitHub.

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

    1pipx install uv
  4. Pakua maktaba.

    1uv sync
  5. Washa mazingira ya mtandaoni.

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

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

Kusoma kutoka kwa mnyororo wa bloku

Hatua inayofuata ni kusoma kutoka kwa mnyororo wa bloku. Ili kufanya hivyo, unahitaji kubadilisha hadi tawi la 02-read-quote kisha utumie uv kuendesha programu.

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

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

Hapa kuna maelezo ya mstari kwa mstari.

1from web3 import Web3
2from web3.contract import Contract
3from decimal import Decimal, ROUND_HALF_UP
4from dataclasses import dataclass
5from datetime import datetime, timezone
6from pprint import pprint
7import time
8import functools
9import sys
Onyesha yote

Ingiza maktaba tunazohitaji. Zinaelezwa hapa chini zinapotumika.

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

Inabadilisha print ya Python na toleo ambalo daima husafisha matokeo mara moja. Hii ni muhimu katika hati inayoendeshwa kwa muda mrefu kwa sababu hatutaki kusubiri masasisho ya hali au matokeo ya utatuzi.

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

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

1BLOCK_TIME_SECONDS = 12
2MINUTE_BLOCKS = int(60 / BLOCK_TIME_SECONDS)
3HOUR_BLOCKS = MINUTE_BLOCKS * 60
4DAY_BLOCKS = HOUR_BLOCKS * 24

Bloku ya mtandao mkuu wa Ethereum kwa kawaida hutokea kila sekunde kumi na mbili, kwa hivyo hizi ni idadi ya bloku tunazotarajia kutokea katika kipindi cha muda. Kumbuka kuwa hii si takwimu kamili. Wakati mpendekezaji wa bloku hayuko hewani, bloku hiyo hurukwa, na muda wa bloku inayofuata ni sekunde 24. Ikiwa tungetaka kupata bloku kamili kwa muhuri wa muda, tungetumia utafutaji wa binary (opens in a new tab). Hata hivyo, hii ni karibu vya kutosha kwa madhumuni yetu. Kutabiri siku zijazo si sayansi kamili.

1CYCLE_BLOCKS = DAY_BLOCKS

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

1# Anwani ya pool tunayosoma
2WETHUSDC_ADDRESS = Web3.to_checksum_address("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")

Thamani za nukuu zinachukuliwa kutoka kwa pool ya Uniswap 3 USDC/WETH katika anwani 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 (opens in a new tab). Anwani hii tayari iko katika fomu ya checksum, lakini ni bora kutumia Web3.to_checksum_address (opens in a new tab) kufanya msimbo uweze kutumika tena.

1POOL_ABI = [
2 { "name": "slot0", ... },
3 { "name": "token0", ... },
4 { "name": "token1", ... },
5]
6
7ERC20_ABI = [
8 { "name": "symbol", ... },
9 { "name": "decimals", ... }
10]
Onyesha yote

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

1w3 = Web3(Web3.HTTPProvider(MAINNET_URL))

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

1@dataclass(frozen=True)
2class ERC20Token:
3 address: str
4 symbol: str
5 decimals: int
6 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 na mkataba. Kumbuka (frozen=True). Katika Python booleans (opens in a new tab) hufafanuliwa kama True au False, herufi kubwa. Darasa hili la data ni frozen, ikimaanisha sehemu zake haziwezi kurekebishwa.

Kumbuka mpangilio. Tofauti na lugha zinazotokana na C (opens in a new tab), Python hutumia mpangilio kuashiria bloku. Mkalimani wa Python anajua kuwa ufafanuzi unaofuata si sehemu ya darasa hili la data kwa sababu hauanzi katika mpangilio sawa na sehemu za darasa la data.

1@dataclass(frozen=True)
2class PoolInfo:
3 address: str
4 token0: ERC20Token
5 token1: ERC20Token
6 contract: Contract
7 asset: str
8 decimal_factor: Decimal = 1

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

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

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

Katika kazi ambayo ni sehemu ya darasa la data, kigezo cha kwanza daima ni self, mfano wa darasa la data uliopiga simu hapa. Hapa kuna kigezo kingine, nambari ya bloku.

1 assert block <= w3.eth.block_number, "Bloku iko katika siku zijazo"

Kama tungeweza kusoma siku zijazo, hatungehitaji AI kwa biashara.

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

Sintaksia ya kupiga simu kazi kwenye EVM kutoka Web3 ni hii: <object ya mkataba>.kazi.<jina la kazi>``().call(<vigezo>). Vigezo vinaweza kuwa vigezo vya kazi ya EVM (ikiwa vipo; hapa havipo) au vigezo vilivyotajwa (opens in a new tab) kwa kurekebisha tabia ya mnyororo wa bloku. Hapa tunatumia moja, block_identifier, kubainisha nambari ya bloku tunayotaka kuendesha ndani.

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

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

Ili kupunguza mahesabu ya onchain, Uniswap v3 haihifadhi kipengele halisi cha ubadilishaji bali mzizi wake wa mraba. Kwa sababu EVM haitumii hesabu ya nambari inayoelea au sehemu, badala ya thamani halisi, jibu ni bei&#x22C5296

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

Bei ghafi tunayopata ni idadi ya tokeni0 tunayopata kwa kila tokeni1. Katika pool yetu, tokeni0 ni USDC (sarafu-imara yenye thamani sawa na dola ya Marekani) na tokeni1 ni WETH (opens in a new tab). Thamani tunayoitaka kweli 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.

1@dataclass(frozen=True)
2class Quote:
3 timestamp: str
4 price: Decimal
5 asset: str

Darasa hili la data linawakilisha nukuu: bei ya mali maalum kwa wakati fulani. Kwa wakati huu, sehemu ya asset haina umuhimu kwa sababu tunatumia pool moja na kwa hivyo tuna mali moja. Hata hivyo, tutaongeza mali zaidi baadaye.

1def read_token(address: str) -> ERC20Token:
2 token = w3.eth.contract(address=address, abi=ERC20_ABI)
3 symbol = token.functions.symbol().call()
4 decimals = token.functions.decimals().call()
5
6 return ERC20Token(
7 address=address,
8 symbol=symbol,
9 decimals=decimals,
10 contract=token
11 )
Onyesha yote

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

1def read_pool(address: str) -> PoolInfo:
2 pool_contract = w3.eth.contract(address=address, abi=POOL_ABI)
3 token0Address = pool_contract.functions.token0().call()
4 token1Address = pool_contract.functions.token1().call()
5 token0 = read_token(token0Address)
6 token1 = read_token(token1Address)
7
8 return PoolInfo(
9 address=address,
10 asset=f"{token1.symbol}/{token0.symbol}",
11 token0=token0,
12 token1=token1,
13 contract=pool_contract,
14 decimal_factor=Decimal(10) ** Decimal(token0.decimals - token1.decimals)
15 )
Onyesha yote

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

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

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

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

Ikiwa nambari ya bloku haikubainishwa, tumia w3.eth.block_number, ambayo ni nambari ya bloku ya hivi karibuni. 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 bloku wakati kazi inafafanuliwa. Katika wakala anayeendeshwa kwa muda mrefu, hili lingekuwa tatizo.

1 block = w3.eth.get_block(block_number)
2 price = pool.get_price(block_number)
3 return Quote(
4 timestamp=datetime.fromtimestamp(block.timestamp, timezone.utc).isoformat(),
5 price=price.quantize(Decimal("0.01")),
6 asset=pool.asset
7 )

Tumia maktaba ya datetime (opens in a new tab) kuiweka katika umbizo linaloweza kusomeka na binadamu na miundo mikubwa ya lugha (LLMs). Tumia Decimal.quantize (opens in a new tab) kuzungusha thamani hadi sehemu mbili za desimali.

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

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

1 quotes = []
2 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 bloku za kupata nukuu ndani yake hutoka kwenye range (opens in a new tab).

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

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

1pool = read_pool(WETHUSDC_ADDRESS)
2quotes = get_quotes(
3 pool,
4 w3.eth.block_number - 12*CYCLE_BLOCKS,
5 w3.eth.block_number,
6 CYCLE_BLOCKS
7)
8
9pprint(quotes)
Onyesha yote

Huu ndio msimbo mkuu wa hati. Soma taarifa ya pool, pata nukuu kumi na mbili, na uzichapishe kwa pprint (opens in a new tab).

Kuunda kidokezo

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

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

Matokeo sasa yatakuwa kidokezo kwa LLM, sawa na:

1Kutokana na nukuu hizi:
2Mali: WETH/USDC
3 2026-01-20T16:34 3016.21
4 .
5 .
6 .
7 2026-02-01T17:49 2299.10
8
9Mali: WBTC/WETH
10 2026-01-20T16:34 29.84
11 .
12 .
13 .
14 2026-02-01T17:50 33.46
15
16
17Ungetarajia thamani ya WETH/USDC iwe nini saa 2026-02-02T17:56?
18
19Toa jibu lako kama nambari moja iliyozungushwa hadi sehemu mbili za desimali,
20bila maandishi mengine yoyote.
Onyesha yote

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

Jinsi kidokezo kinavyoonekana

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

  1. Taarifa. LLM zina taarifa nyingi kutoka kwa mafunzo yao, lakini kwa kawaida hazina za hivi karibuni. Hii ndiyo sababu tunahitaji kupata nukuu za hivi karibuni hapa. Kuongeza taarifa kwenye kidokezo kunaitwa kizazi kilichoongezwa kwa urejeshaji (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 ilivyoyafikia. Hii ni bora kwa wanadamu, lakini programu ya kompyuta inahitaji tu mstari wa chini.

Maelezo ya msimbo

Huu hapa msimbo mpya.

1from datetime import datetime, timezone, timedelta

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

1# Anwani za pools tunazosoma
2WETHUSDC_ADDRESS = Web3.to_checksum_address("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
3WETHWBTC_ADDRESS = Web3.to_checksum_address("0xCBCdF9626bC03E24f779434178A73a0B4bad62eD")

Tuna pools mbili tunazohitaji kusoma.

1@dataclass(frozen=True)
2class PoolInfo:
3 .
4 .
5 .
6 reverse: bool = False
7
8 def get_price(self, block: int) -> Decimal:
9 assert block <= w3.eth.block_number, "Bloku iko katika siku zijazo"
10 sqrt_price_x96 = Decimal(self.contract.functions.slot0().call(block_identifier=block)[0])
11 raw_price = (sqrt_price_x96 / Decimal(2**96)) ** 2 # (tokeni1 kwa kila tokeni0)
12 if self.reverse:
13 return 1/(raw_price * self.decimal_factor)
14 else:
15 return raw_price * self.decimal_factor
Onyesha yote

Katika pool ya WETH/USDC, tunataka kujua ni tokeni0 (USDC) ngapi tunahitaji kununua tokeni1 moja (WETH). Katika pool ya WETH/WBTC, tunataka kujua ni tokeni1 (WETH) ngapi tunahitaji kununua tokeni0 moja (WBTC, ambayo ni Bitcoin iliyofungwa). Tunahitaji kufuatilia ikiwa uwiano wa pool unahitaji kubadilishwa.

1def read_pool(address: str, reverse: bool = False) -> PoolInfo:
2 .
3 .
4 .
5
6 return PoolInfo(
7 .
8 .
9 .
10
11 asset= f"{token1.symbol}/{token0.symbol}" if reverse else f"{token0.symbol}/{token1.symbol}",
12 reverse=reverse
13 )
Onyesha yote

Ili kujua kama pool inahitaji kubadilishwa, tunapata hiyo kama ingizo kwa read_pool. Pia, alama ya mali inahitaji kuwekwa kwa usahihi.

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

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

Kazi hii inaunda kamba inayoweka umbizo la orodha ya vitu vya Quote, ikidhani vyote vinatumika kwa mali moja.

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

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

1Kutokana na nukuu hizi:
2{
3 functools.reduce(lambda acc, q: acc + '\n' + q,
4 map(lambda q: format_quotes(q), quotes))
5}

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

1Ungetarajia thamani ya {asset} iwe nini saa {expected_time}?
2
3Toa jibu lako kama nambari moja iliyozungushwa hadi sehemu mbili za desimali,
4bila maandishi mengine yoyote.
5 """

Sehemu iliyobaki ya kidokezo iko kama inavyotarajiwa.

1wethusdc_pool = read_pool(WETHUSDC_ADDRESS, True)
2wethusdc_quotes = get_quotes(
3 wethusdc_pool,
4 w3.eth.block_number - 12*CYCLE_BLOCKS,
5 w3.eth.block_number,
6 CYCLE_BLOCKS,
7)
8
9wethwbtc_pool = read_pool(WETHWBTC_ADDRESS)
10wethwbtc_quotes = get_quotes(
11 wethwbtc_pool,
12 w3.eth.block_number - 12*CYCLE_BLOCKS,
13 w3.eth.block_number,
14 CYCLE_BLOCKS
15)
Onyesha yote

Pitia pools zote mbili na upate nukuu kutoka kwa zote mbili.

1future_time = (datetime.now(timezone.utc) + timedelta(days=1)).isoformat()[0:16]
2
3print(make_prompt(wethusdc_quotes + wethwbtc_quotes, future_time, wethusdc_pool.asset))

Bainisha nukta ya wakati wa siku zijazo ambayo tunataka makadirio, na unda kidokezo.

Kuunganisha na LLM

Kisha, tunatoa kidokezo kwa LLM halisi na kupokea thamani inayotarajiwa ya siku zijazo. 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 fedha kwenye akaunti (opens in a new tab)—kiwango cha chini wakati wa kuandika ni $5

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

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

    1export OPENAI_API_KEY=sk-<sehemu iliyobaki ya ufunguo inaenda hapa>
  5. Kagua na endesha wakala

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

Huu hapa msimbo mpya.

1from openai import OpenAI
2
3open_ai = OpenAI() # Wateja husoma kigeugeu cha mazingira cha OPENAI_API_KEY

Ingiza na uanzishe API ya OpenAI.

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

Piga simu API ya OpenAI (open_ai.chat.completions.create) kuunda jibu.

1expected_price = Decimal(response.choices[0].message.content.strip())
2current_price = wethusdc_quotes[-1].price
3
4print ("Bei ya sasa:", wethusdc_quotes[-1].price)
5print(f"Mnamo {future_time}, bei inayotarajiwa: {expected_price} USD")
6
7if (expected_price > current_price):
8 print(f"Nunua, ninatarajia bei itapanda kwa {expected_price - current_price} USD")
9else:
10 print(f"Uza, ninatarajia bei itashuka kwa {current_price - expected_price} USD")
Onyesha yote

Toa bei na toa pendekezo la kununua au kuuza.

Kupima utabiri

Sasa kwa kuwa tunaweza kutengeneza utabiri, tunaweza pia kutumia data ya kihistoria kutathmini kama tunazalisha utabiri muhimu.

1uv run test-predictor.py

Matokeo yanayotarajiwa ni sawa na:

1Utabiri wa 2026-01-05T19:50: ilitabiriwa 3138.93 USD, halisi 3218.92 USD, kosa 79.99 USD
2Utabiri wa 2026-01-06T19:56: ilitabiriwa 3243.39 USD, halisi 3221.08 USD, kosa 22.31 USD
3Utabiri wa 2026-01-07T20:02: ilitabiriwa 3223.24 USD, halisi 3146.89 USD, kosa 76.35 USD
4Utabiri wa 2026-01-08T20:11: ilitabiriwa 3150.47 USD, halisi 3092.04 USD, kosa 58.43 USD
5.
6.
7.
8Utabiri wa 2026-01-31T22:33: ilitabiriwa 2637.73 USD, halisi 2417.77 USD, kosa 219.96 USD
9Utabiri wa 2026-02-01T22:41: ilitabiriwa 2381.70 USD, halisi 2318.84 USD, kosa 62.86 USD
10Utabiri wa 2026-02-02T22:49: ilitabiriwa 2234.91 USD, halisi 2349.28 USD, kosa 114.37 USD
11Kosa la wastani la utabiri juu ya utabiri 29: 83.87103448275862068965517241 USD
12Mabadiliko ya wastani kwa kila pendekezo: 4.787931034482758620689655172 USD
13Tofauti ya kawaida ya mabadiliko: 104.42 USD
14Siku za faida: 51.72%
15Siku za hasara: 48.28%
Onyesha yote

Sehemu kubwa ya mpimaji ni sawa na wakala, lakini hapa kuna sehemu ambazo ni mpya au zilizobadilishwa.

1CYCLES_FOR_TEST = 40 # Kwa jaribio la nyuma, ni mizunguko mingapi tunayopima
2
3# Pata nukuu nyingi
4wethusdc_pool = read_pool(WETHUSDC_ADDRESS, True)
5wethusdc_quotes = get_quotes(
6 wethusdc_pool,
7 w3.eth.block_number - CYCLE_BLOCKS*CYCLES_FOR_TEST,
8 w3.eth.block_number,
9 CYCLE_BLOCKS,
10)
11
12wethwbtc_pool = read_pool(WETHWBTC_ADDRESS)
13wethwbtc_quotes = get_quotes(
14 wethwbtc_pool,
15 w3.eth.block_number - CYCLE_BLOCKS*CYCLES_FOR_TEST,
16 w3.eth.block_number,
17 CYCLE_BLOCKS
18)
Onyesha yote

Tunaangalia siku CYCLES_FOR_TEST (zilizobainishwa kama 40 hapa) zilizopita.

1# Unda utabiri na uangalie dhidi ya historia halisi
2
3total_error = Decimal(0)
4changes = []

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

Ili kuelewa ya pili, changes, tunahitaji kukumbuka kusudi la wakala. 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 tukanunua ETH kulingana na pendekezo hilo, na bei ikashuka hadi $1990.

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

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

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

Tumia slices (opens in a new tab) kupata idadi sawa ya sampuli kama idadi ambayo wakala hutumia. Msimbo kati ya hapa na sehemu inayofuata ni msimbo uleule wa kupata utabiri tulio nao katika wakala.

1 predicted_price = Decimal(response.choices[0].message.content.strip())
2 real_price = wethusdc_quotes[index+CYCLES_BACK].price
3 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 kubaini kama pendekezo lilikuwa la kununua au kuuza.

1 error = abs(predicted_price - real_price)
2 total_error += error
3 print (f"Utabiri wa {prediction_time}: ilitabiriwa {predicted_price} USD, halisi {real_price} USD, kosa {error} USD")

Tafuta kosa, na uliongeze kwenye jumla.

1 recomended_action = 'buy' if predicted_price > prediction_time_price else 'sell'
2 price_increase = real_price - prediction_time_price
3 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 kubaini pendekezo, kisha tutathmini jinsi bei halisi ilivyobadilika, na kama pendekezo lilileta pesa (mabadiliko chanya) au liligharimu pesa (mabadiliko hasi).

1print (f"Kosa la wastani la utabiri juu ya utabiri {len(wethusdc_quotes)-CYCLES_BACK}: {total_error / Decimal(len(wethusdc_quotes)-CYCLES_BACK)} USD")
2
3length_changes = Decimal(len(changes))
4mean_change = sum(changes, Decimal(0)) / length_changes
5print (f"Mabadiliko ya wastani kwa kila pendekezo: {mean_change} USD")
6var = sum((x - mean_change) ** 2 for x in changes) / length_changes
7print (f"Tofauti ya kawaida ya mabadiliko: {var.sqrt().quantize(Decimal("0.01"))} USD")

Ripoti matokeo.

1print (f"Siku za faida: {len(list(filter(lambda x: x > 0, changes)))/length_changes:.2%}")
2print (f"Siku za hasara: {len(list(filter(lambda x: x < 0, changes)))/length_changes:.2%}")

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

Kuwasilisha miamala

Sasa tunahitaji kuwasilisha miamala. Hata hivyo, sitaki kutumia pesa halisi kwa wakati huu, kabla mfumo haujathibitishwa. Badala yake, tutaunda uma wa ndani wa mtandao mkuu, na "kufanya biashara" kwenye mtandao huo.

Hizi ni hatua za kuunda uma wa ndani na kuwezesha biashara.

  1. Sakinisha Foundry (opens in a new tab)

  2. Anzisha anvil (opens in a new tab)

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

    anvil inasikiliza kwenye URL ya 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 bloku.

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

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

    Vigeugeu viwili vya chini ni njia za Uniswap v3 zinazohitajika kubadilisha kati ya WETH na USDC.

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

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

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

    Simu ya approve inaunda ruhusa inayoruhusu SwapRouter kutumia baadhi ya tokeni zetu. Mikataba haiwezi kufuatilia matukio, kwa hivyo ikiwa tutahamisha tokeni moja kwa moja kwenye mkataba wa SwapRouter, haitajua imelipwa. Badala yake, tunaruhusu mkataba wa SwapRouter kutumia kiasi fulani, na kisha SwapRouter hufanya hivyo. Hii inafanywa kupitia kazi inayoitwa na SwapRouter, kwa hivyo inajua kama imefanikiwa.

  7. Thibitisha una tokeni za kutosha za aina zote mbili.

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

Sasa kwa kuwa tuna WETH na USDC, tunaweza kweli kuendesha wakala.

1git checkout 05-trade
2uv run agent.py

Matokeo yataonekana sawa na:

1(ai-trading-agent) qbzzt@Ori-Cloudnomics:~/260215-ai-agent$ uv run agent.py
2Bei ya sasa: 1843.16
3Mnamo 2026-02-06T23:07, bei inayotarajiwa: 1724.41 USD
4Salio za akaunti kabla ya biashara:
5Salio la USDC: 927301.578272
6Salio la WETH: 500
7Uza, ninatarajia bei itashuka kwa 118.75 USD
8Muamala wa idhini umetumwa: 74e367ddbb407c1aaf567d87aa5863049991b1d2aa092b6b85195d925e2bd41f
9Muamala wa idhini umefanikiwa.
10Muamala wa uuzaji umetumwa: fad1bcf938585c9e90364b26ac7a80eea9efd34c37e5db81e58d7655bcae28bf
11Muamala wa uuzaji umefanikiwa.
12Salio za akaunti baada ya biashara:
13Salio la USDC: 929143.797116
14Salio la WETH: 499
Onyesha yote

Ili kuitumia kweli, unahitaji mabadiliko madogo.

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

Maelezo ya msimbo

Huu hapa msimbo mpya.

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

Vigeugeu vilevile tulivyotumia katika hatua ya 4.

1WETH_TRADE_AMOUNT=1

Kiasi cha kufanya biashara.

1ERC20_ABI = [
2 { "name": "symbol", ... },
3 { "name": "decimals", ... },
4 { "name": "balanceOf", ...},
5 { "name": "approve", ...}
6]

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

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

Katika ABI ya SwapRouter tunahitaji tu exactInput. Kuna kazi inayohusiana, exactOutput, tunaweza kuitumia kununua WETH moja hasa, lakini kwa urahisi tunatumia tu exactInput katika visa vyote viwili.

1account = w3.eth.account.from_key(PRIVATE_KEY)
2swap_router = w3.eth.contract(
3 address=SWAP_ROUTER_ADDRESS,
4 abi=SWAP_ROUTER_ABI
5)

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

1def txn_params() -> dict:
2 return {
3 "from": account.address,
4 "value": 0,
5 "gas": 300000,
6 "nonce": w3.eth.get_transaction_count(account.address),
7 }

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

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

Idhinisha ruhusa ya tokeni kwa SwapRouter.

1 txn = contract.functions.approve(SWAP_ROUTER_ADDRESS, amount).build_transaction(txn_params())
2 signed_txn = w3.eth.account.sign_transaction(txn, private_key=PRIVATE_KEY)
3 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) kusaini muamala, kwa kutumia PRIVATE_KEY. Mwisho, tunatumia w3.eth.send_raw_transaction (opens in a new tab) kutuma muamala.

1 print(f"Muamala wa idhini umetumwa: {tx_hash.hex()}")
2 w3.eth.wait_for_transaction_receipt(tx_hash)
3 print("Muamala wa idhini umefanikiwa.")

w3.eth.wait_for_transaction_receipt (opens in a new tab) husubiri hadi muamala utakapofanikiwa. Inarudisha risiti ikihitajika.

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

Hizi ni vigezo wakati wa kuuza WETH.

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

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

1def buy(quote: Quote):
2 buy_params = make_buy_params(quote)
3 approve_token(wethusdc_pool.token0.contract, buy_params["amountIn"])
4 txn = swap_router.functions.exactInput(buy_params).build_transaction(txn_params())
5 signed_txn = w3.eth.account.sign_transaction(txn, private_key=PRIVATE_KEY)
6 tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)
7 print(f"Muamala wa ununuzi umetumwa: {tx_hash.hex()}")
8 w3.eth.wait_for_transaction_receipt(tx_hash)
9 print("Muamala wa ununuzi umefanikiwa.")
10
11
12def sell():
13 approve_token(wethusdc_pool.token1.contract,
14 WETH_TRADE_AMOUNT * 10**wethusdc_pool.token1.decimals)
15 txn = swap_router.functions.exactInput(SELL_PARAMS).build_transaction(txn_params())
16 signed_txn = w3.eth.account.sign_transaction(txn, private_key=PRIVATE_KEY)
17 tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)
18 print(f"Muamala wa uuzaji umetumwa: {tx_hash.hex()}")
19 w3.eth.wait_for_transaction_receipt(tx_hash)
20 print("Muamala wa uuzaji umefanikiwa.")
Onyesha yote

Kazi za buy() na sell() karibu zinafanana. Kwanza tunakubali ruhusa ya kutosha kwa SwapRouter, na kisha tunaipiga simu kwa njia na kiasi sahihi.

1def balances():
2 token0_balance = wethusdc_pool.token0.contract.functions.balanceOf(account.address).call()
3 token1_balance = wethusdc_pool.token1.contract.functions.balanceOf(account.address).call()
4
5 print(f"{wethusdc_pool.token0.symbol} Salio: {Decimal(token0_balance) / Decimal(10 ** wethusdc_pool.token0.decimals)}")
6 print(f"{wethusdc_pool.token1.symbol} Salio: {Decimal(token1_balance) / Decimal(10 ** wethusdc_pool.token1.decimals)}")

Ripoti salio la mtumiaji katika sarafu zote mbili.

1print("Salio za akaunti kabla ya biashara:")
2balances()
3
4if (expected_price > current_price):
5 print(f"Nunua, ninatarajia bei itapanda kwa {expected_price - current_price} USD")
6 buy(wethusdc_quotes[-1])
7else:
8 print(f"Uza, ninatarajia bei itashuka kwa {current_price - expected_price} USD")
9 sell()
10
11print("Salio za akaunti baada ya biashara:")
12balances()
Onyesha yote

Wakala huyu kwa sasa anafanya kazi mara moja tu. Hata hivyo, unaweza kuibadilisha ifanye kazi mfululizo ama kwa kuiendesha 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 iwe wakati wa mzunguko unaofuata.

Maboresho yanayowezekana

Hili si toleo kamili la uzalishaji; ni mfano tu wa kufundisha misingi. Hapa kuna maoni kadhaa ya maboresho.

Biashara yenye akili zaidi

Kuna ukweli mbili muhimu ambazo wakala anapuuza wakati wa kuamua nini cha kufanya.

  • Ukubwa wa mabadiliko yanayotarajiwa. Wakala anauza kiasi kisichobadilika cha WETH ikiwa bei inatarajiwa kushuka, bila kujali ukubwa wa kushuka. Inaweza kusemwa, ingekuwa bora kupuuza mabadiliko madogo na kuuza kulingana na kiasi gani tunatarajia bei itashuka.
  • 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 umejifunua vya kutosha, na hakuna haja ya kununua zaidi. Kinyume chake ni kweli ikiwa unatarajia bei itashuka.

Vipi ikiwa unataka kuweka mkakati wako wa biashara kuwa siri?

Wachuuzi wa AI wanaweza kuona maswali unayotuma kwa LLM zao, ambayo inaweza kufichua mfumo wa biashara wa kitaalam uliouendeleza na wakala wako. Mfumo wa biashara ambao watu wengi sana hutumia hauna thamani kwa sababu watu wengi sana hujaribu kununua wakati unapotaka kununua (na bei inapanda) na kujaribu kuuza wakati unapotaka 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 wakala wa AI

Unaweza kutoa hoja nzuri kwamba hii ni boti ya AI, si wakala wa AI. Inatekeleza mkakati rahisi ambao unategemea taarifa zilizofafanuliwa awali. Tunaweza kuwezesha uboreshaji wa kibinafsi, kwa mfano, kwa kutoa orodha ya pools za Uniswap v3 na thamani zao za hivi karibuni na kuuliza ni mchanganyiko gani una thamani bora ya utabiri.

Ulinzi wa slippage

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

Ili kutekeleza ulinzi wa slippage, 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 wakala wa AI. Huu si muhtasari kamili wa somo; kuna vitabu vizima vilivyojitolea kwa hilo, lakini hii inatosha kukuanzisha. Kila la kheri!

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

Ukurasa ulihaririwa mwisho: 10 Februari 2026

Umesaidika na mafunzo haya?