এড়িয়ে গিয়ে মূল কন্টেন্টে যান

ইথেরিয়ামে আপনার নিজস্ব AI ট্রেডিং এজেন্ট তৈরি করুন

AI
ট্রেডিং
এজেন্ট
python
মধ্যবর্তী
Ori Pomerantz
১৩ ফেব্রুয়ারী, ২০২৬
23 মিনিটের পাঠ

এই টিউটোরিয়ালে আপনি একটি সহজ AI ট্রেডিং এজেন্ট কীভাবে তৈরি করতে হয় তা শিখবেন। এই এজেন্টটি এই ধাপগুলো ব্যবহার করে কাজ করে:

  1. একটি টোকেনের বর্তমান এবং অতীতের মূল্য, সেইসাথে অন্যান্য সম্ভাব্য প্রাসঙ্গিক তথ্য পড়ুন
  2. এই তথ্যের সাথে একটি কোয়েরি তৈরি করুন, সাথে ব্যাকগ্রাউন্ড তথ্য যোগ করে ব্যাখ্যা করুন যে এটি কীভাবে প্রাসঙ্গিক হতে পারে
  3. কোয়েরি জমা দিন এবং একটি অনুমানিত মূল্য ফেরত পান
  4. সুপারিশের ভিত্তিতে ট্রেড করুন
  5. অপেক্ষা করুন এবং পুনরাবৃত্তি করুন

এই এজেন্টটি দেখায় কীভাবে তথ্য পড়তে হয়, এটিকে একটি কোয়েরিতে রূপান্তর করতে হয় যা একটি ব্যবহারযোগ্য উত্তর দেয় এবং সেই উত্তরটি ব্যবহার করতে হয়। এগুলো সবই একটি AI এজেন্টের জন্য প্রয়োজনীয় ধাপ। এই এজেন্টটি Python-এ প্রয়োগ করা হয়েছে কারণ এটি AI-তে ব্যবহৃত সবচেয়ে সাধারণ ভাষা।

এটা কেন করবেন?

স্বয়ংক্রিয় ট্রেডিং এজেন্ট ডেভেলপারদের একটি ট্রেডিং কৌশল নির্বাচন এবং কার্যকর করার সুযোগ দেয়। AI এজেন্ট আরও জটিল এবং গতিশীল ট্রেডিং কৌশলের সুযোগ দেয়, সম্ভাব্যভাবে এমন তথ্য এবং অ্যালগরিদম ব্যবহার করে যা ডেভেলপার ব্যবহার করার কথাও ভাবেনি।

টুলস

এই টিউটোরিয়ালটি কোট এবং ট্রেডিংয়ের জন্য Python (opens in a new tab), Web3 লাইব্রেরি (opens in a new tab) এবং Uniswap v3 (opens in a new tab) ব্যবহার করে।

কেন Python?

AI-এর জন্য সবচেয়ে বেশি ব্যবহৃত ভাষা হল Python (opens in a new tab), তাই আমরা এখানে এটি ব্যবহার করেছি। আপনি Python না জানলেও চিন্তা করবেন না। ভাষাটি খুব স্পষ্ট, এবং আমি ব্যাখ্যা করব এটি ঠিক কী করে।

Web3 লাইব্রেরি (opens in a new tab) হল সবচেয়ে সাধারণ Python ইথেরিয়াম API। এটি ব্যবহার করা বেশ সহজ।

ব্লকচেইনে ট্রেডিং

অনেক ডিস্ট্রিবিউটেড এক্সচেঞ্জ (DEX) আছে যা আপনাকে ইথেরিয়ামে টোকেন ট্রেড করার সুযোগ দেয়। তবে, আর্বিট্রেজ-এর কারণে তাদের এক্সচেঞ্জ রেট প্রায় একই রকম থাকে।

Uniswap (opens in a new tab) একটি বহুল ব্যবহৃত DEX যা আমরা কোট (টোকেনের আপেক্ষিক মান দেখতে) এবং ট্রেড উভয়ের জন্য ব্যবহার করতে পারি।

OpenAI

একটি বড় ল্যাঙ্গুয়েজ মডেলের জন্য, আমি OpenAI (opens in a new tab) দিয়ে শুরু করতে চেয়েছি। এই টিউটোরিয়ালের অ্যাপ্লিকেশনটি চালানোর জন্য আপনাকে API অ্যাক্সেসের জন্য অর্থপ্রদান করতে হবে। $5-এর ন্যূনতম পেমেন্ট যথেষ্টর চেয়েও বেশি।

ডেভেলপমেন্ট, ধাপে ধাপে

ডেভেলপমেন্টকে সহজ করার জন্য, আমরা ধাপে ধাপে এগোব। প্রতিটি ধাপ GitHub-এর একটি ব্রাঞ্চ।

শুরু করা যাক

UNIX বা Linux-এর অধীনে শুরু করার জন্য কিছু ধাপ রয়েছে (এর মধ্যে WSL (opens in a new tab) অন্তর্ভুক্ত)

  1. আপনার কাছে যদি এটি আগে থেকে না থাকে, তাহলে Python (opens in a new tab) ডাউনলোড এবং ইনস্টল করুন।

  2. GitHub রিপোজিটরিটি ক্লোন করুন।

    1git clone https://github.com/qbzzt/260215-ai-agent.git -b 01-getting-started
    2cd 260215-ai-agent
  3. uv (opens in a new tab) ইনস্টল করুন। আপনার সিস্টেমে কমান্ডটি ভিন্ন হতে পারে।

    1pipx install uv
  4. লাইব্রেরিগুলো ডাউনলোড করুন।

    1uv sync
  5. ভার্চুয়াল এনভায়রনমেন্ট সক্রিয় করুন।

    1source .venv/bin/activate
  6. Python এবং Web3 সঠিকভাবে কাজ করছে কিনা তা যাচাই করতে, python3 চালান এবং এটিকে এই প্রোগ্রামটি সরবরাহ করুন। আপনি এটি >>> প্রম্পটে প্রবেশ করতে পারেন; একটি ফাইল তৈরি করার প্রয়োজন নেই।

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

ব্লকচেইন থেকে পড়া

পরবর্তী ধাপ হল ব্লকচেইন থেকে পড়া। এটি করার জন্য, আপনাকে 02-read-quote ব্রাঞ্চে পরিবর্তন করতে হবে এবং তারপর প্রোগ্রামটি চালানোর জন্য uv ব্যবহার করতে হবে।

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

আপনার একটি Quote অবজেক্টের তালিকা পাওয়া উচিত, প্রতিটিতে একটি টাইমস্ট্যাম্প, একটি মূল্য এবং অ্যাসেট (বর্তমানে সবসময় WETH/USDC) থাকবে।

এখানে একটি লাইন-বাই-লাইন ব্যাখ্যা দেওয়া হলো।

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
সবকটি দেখুন

আমাদের প্রয়োজনীয় লাইব্রেরিগুলি আমদানি করুন। এগুলি ব্যবহার করার সময় নীচে ব্যাখ্যা করা হয়েছে।

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

Python-এর print-কে এমন একটি সংস্করণ দিয়ে প্রতিস্থাপন করে যা সবসময় অবিলম্বে আউটপুট ফ্লাশ করে। এটি একটি দীর্ঘ-চলমান স্ক্রিপ্টে উপযোগী কারণ আমরা স্ট্যাটাস আপডেট বা ডিবাগিং আউটপুটের জন্য অপেক্ষা করতে চাই না।

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

মেইননেটে যাওয়ার জন্য একটি URL। আপনি নোড অ্যাজ এ সার্ভিস থেকে একটি পেতে পারেন বা 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

একটি ইথেরিয়াম মেইননেট ব্লক সাধারণত প্রতি বারো সেকেন্ডে ঘটে, তাই এগুলি হল ব্লকের সংখ্যা যা আমরা একটি সময়কালে ঘটার আশা করি। লক্ষ্য করুন যে এটি একটি সঠিক সংখ্যা নয়। যখন ব্লক প্রস্তাবক ডাউন থাকে, তখন সেই ব্লকটি এড়িয়ে যাওয়া হয় এবং পরবর্তী ব্লকের জন্য সময় হল 24 সেকেন্ড। আমরা যদি একটি টাইমস্ট্যাম্পের জন্য সঠিক ব্লক পেতে চাই, তাহলে আমরা বাইনারি সার্চ (opens in a new tab) ব্যবহার করব। তবে, এটি আমাদের উদ্দেশ্যের জন্য যথেষ্ট কাছাকাছি। ভবিষ্যদ্বাণী করা কোনো সঠিক বিজ্ঞান নয়।

1CYCLE_BLOCKS = DAY_BLOCKS

চক্রের আকার। আমরা প্রতি চক্রে একবার কোট পর্যালোচনা করি এবং পরবর্তী চক্রের শেষে মান অনুমান করার চেষ্টা করি।

1# আমরা যে পুলটি পড়ছি তার অ্যাড্রেস
2WETHUSDC_ADDRESS = Web3.to_checksum_address("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")

কোট মানগুলি Uniswap 3 USDC/WETH পুল থেকে 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 (opens in a new tab) অ্যাড্রেসে নেওয়া হয়েছে। এই অ্যাড্রেসটি ইতিমধ্যেই চেকসাম ফর্মে আছে, কিন্তু কোডটিকে পুনঃব্যবহারযোগ্য করতে Web3.to_checksum_address (opens in a new tab) ব্যবহার করা ভাল।

1POOL_ABI = [
2 { "name": "slot0", ... },
3 { "name": "token0", ... },
4 { "name": "token1", ... },
5]
6
7ERC20_ABI = [
8 { "name": "symbol", ... },
9 { "name": "decimals", ... }
10]
সবকটি দেখুন

এগুলো হল দুটি কন্ট্র্যাক্টের ABI (opens in a new tab) যার সাথে আমাদের যোগাযোগ করতে হবে। কোডটি সংক্ষিপ্ত রাখতে, আমরা শুধুমাত্র সেই ফাংশনগুলো অন্তর্ভুক্ত করি যা আমাদের কল করতে হবে।

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

Web3 (opens in a new tab) লাইব্রেরি শুরু করুন এবং একটি ইথেরিয়াম নোডের সাথে সংযোগ স্থাপন করুন।

1@dataclass(frozen=True)
2class ERC20Token:
3 address: str
4 symbol: str
5 decimals: int
6 contract: Contract

এটি Python-এ একটি ডেটা ক্লাস তৈরি করার একটি উপায়। কন্ট্র্যাক্টের সাথে সংযোগ স্থাপন করতে Contract (opens in a new tab) ডেটা টাইপ ব্যবহার করা হয়। (frozen=True) লক্ষ্য করুন। Python-এ বুলিয়ান (opens in a new tab)-কে True বা False হিসেবে সংজ্ঞায়িত করা হয়, যা ক্যাপিটালাইজড। এই ডেটা ক্লাসটি frozen, যার মানে ফিল্ডগুলি পরিবর্তন করা যাবে না।

ইনডেনটেশন লক্ষ্য করুন। C-থেকে উদ্ভূত ভাষা (opens in a new tab)-এর বিপরীতে, Python ব্লক বোঝাতে ইনডেনটেশন ব্যবহার করে। Python ইন্টারপ্রেটার জানে যে নিম্নলিখিত সংজ্ঞাটি এই ডেটা ক্লাসের অংশ নয় কারণ এটি ডেটা ক্লাসের ফিল্ডগুলির মতো একই ইনডেনটেশন থেকে শুরু হয় না।

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

দশমিক ভগ্নাংশ সঠিকভাবে পরিচালনা করার জন্য Decimal (opens in a new tab) টাইপ ব্যবহার করা হয়।

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

এটি Python-এ একটি ফাংশন সংজ্ঞায়িত করার উপায়। সংজ্ঞাটি ইনডেন্ট করা হয়েছে এটি দেখানোর জন্য যে এটি এখনও PoolInfo-এর অংশ।

একটি ডেটা ক্লাসের অংশ এমন একটি ফাংশনে প্রথম প্যারামিটারটি সর্বদা self, যা এখানে কল করা ডেটা ক্লাস ইনস্ট্যান্স। এখানে আরেকটি প্যারামিটার আছে, ব্লক নম্বর।

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

আমরা যদি ভবিষ্যৎ পড়তে পারতাম, তবে ট্রেডিংয়ের জন্য AI-এর প্রয়োজন হতো না।

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

Web3 থেকে EVM-এ একটি ফাংশন কল করার সিনট্যাক্স হল: <contract object>.functions.<function name>"().call(<parameters>)। প্যারামিটারগুলো EVM ফাংশনের প্যারামিটার হতে পারে (যদি থাকে; এখানে নেই) অথবা ব্লকচেইন আচরণ পরিবর্তন করার জন্য নামযুক্ত প্যারামিটার (opens in a new tab) হতে পারে। এখানে আমরা block_identifier ব্যবহার করি, যা ব্লক নম্বর নির্দিষ্ট করে যেখানে আমরা চালাতে চাই।

ফলাফল হল এই struct, অ্যারে ফর্মে (opens in a new tab)। প্রথম মানটি দুটি টোকেনের মধ্যে এক্সচেঞ্জ রেটের একটি ফাংশন।

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

অনচেইন গণনা কমাতে, Uniswap v3 আসল এক্সচেঞ্জ ফ্যাক্টর সংরক্ষণ করে না বরং এর বর্গমূল সংরক্ষণ করে। যেহেতু EVM ফ্লোটিং পয়েন্ট ম্যাথ বা ভগ্নাংশ সমর্থন করে না, তাই আসল মানের পরিবর্তে, প্রতিক্রিয়া হল price&#x22C5296

1 # (টোকেন0 প্রতি টোকেন1)
2 return 1/(raw_price * self.decimal_factor)

আমরা যে কাঁচা মূল্য পাই তা হল টোকেন1-এর প্রতি আমরা কত টোকেন0 পাব তার সংখ্যা। আমাদের পুলে টোকেন0 হল USDC (মার্কিন ডলারের সমান মূল্যের স্টেবলকয়েন) এবং টোকেন1 হল WETH (opens in a new tab)। আমরা আসলে যে মানটি চাই তা হল প্রতি WETH-এর জন্য ডলারের সংখ্যা, এর বিপরীত নয়।

ডেসিমাল ফ্যাক্টর হলো দুটি টোকেনের ডেসিমাল ফ্যাক্টর (opens in a new tab) এর মধ্যকার অনুপাত।

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

এই ডেটা ক্লাসটি একটি কোট উপস্থাপন করে: একটি নির্দিষ্ট সময়ে একটি নির্দিষ্ট অ্যাসেটের মূল্য। এই মুহূর্তে, asset ফিল্ডটি অপ্রাসঙ্গিক কারণ আমরা একটি একক পুল ব্যবহার করি এবং তাই একটি একক অ্যাসেট আছে। তবে, আমরা পরে আরও অ্যাসেট যোগ করব।

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 )
সবকটি দেখুন

এই ফাংশনটি একটি অ্যাড্রেস নেয় এবং সেই অ্যাড্রেসে থাকা টোকেন কন্ট্র্যাক্ট সম্পর্কে তথ্য প্রদান করে। একটি নতুন Web3 Contract (opens in a new tab) তৈরি করতে, আমরা w3.eth.contract-কে অ্যাড্রেস এবং ABI প্রদান করি।

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 )
সবকটি দেখুন

এই ফাংশনটি একটি নির্দিষ্ট পুল (opens in a new tab) সম্পর্কে আমাদের প্রয়োজনীয় সবকিছু প্রদান করে। সিনট্যাক্স f"<string>" একটি ফরম্যাটেড স্ট্রিং (opens in a new tab)

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

একটি Quote অবজেক্ট পান। block_number-এর ডিফল্ট মান হল None (কোনো মান নেই)।

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

যদি কোনো ব্লক নম্বর নির্দিষ্ট করা না হয়, তাহলে w3.eth.block_number ব্যবহার করুন, যা সর্বশেষ ব্লক নম্বর। এটি একটি if স্টেটমেন্টের (opens in a new tab) সিনট্যাক্স।

এটা দেখে মনে হতে পারে যে ডিফল্ট হিসেবে w3.eth.block_number সেট করাই ভালো ছিল, কিন্তু সেটা ঠিকভাবে কাজ করে না কারণ এটা ফাংশনটি সংজ্ঞায়িত করার সময়ের ব্লক নম্বর হতো। একটি দীর্ঘ-চলমান এজেন্টে, এটি একটি সমস্যা হবে।

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 )

মানুষ এবং বড় ভাষার মডেলের (LLM) জন্য পঠনযোগ্য ফরম্যাটে ফরম্যাট করতে datetime লাইব্রেরি (opens in a new tab) ব্যবহার করুন। মানটিকে দুই দশমিক স্থানে রাউন্ড করতে Decimal.quantize (opens in a new tab) ব্যবহার করুন।

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

Python-এ আপনি list[<type>] ব্যবহার করে একটি তালিকা (opens in a new tab) সংজ্ঞায়িত করেন যা শুধুমাত্র একটি নির্দিষ্ট টাইপ ধারণ করতে পারে।

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

Python-এ একটি for লুপ (opens in a new tab) সাধারণত একটি তালিকার উপর পুনরাবৃত্তি করে। কোট খুঁজে বের করার জন্য ব্লক নম্বরের তালিকাটি range (opens in a new tab) থেকে আসে।

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

প্রতিটি ব্লক নম্বরের জন্য, একটি Quote অবজেক্ট পান এবং এটিকে quotes তালিকায় যোগ করুন। তারপর সেই তালিকাটি ফেরত দিন।

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)
সবকটি দেখুন

এটি স্ক্রিপ্টের মূল কোড। পুল তথ্য পড়ুন, বারোটি কোট পান, এবং সেগুলোকে pprint (opens in a new tab) করুন।

একটি প্রম্পট তৈরি করা

এরপরে, আমাদের এই কোটগুলোর তালিকাকে একটি LLM-এর জন্য প্রম্পটে রূপান্তর করতে হবে এবং একটি প্রত্যাশিত ভবিষ্যৎ মান পেতে হবে।

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

আউটপুট এখন একটি LLM-এর জন্য একটি প্রম্পট হবে, যা দেখতে অনেকটা এইরকম:

1এই কোটগুলো দেওয়া আছে:
2অ্যাসেট: WETH/USDC
3 2026-01-20T16:34 3016.21
4 .
5 .
6 .
7 2026-02-01T17:49 2299.10
8
9অ্যাসেট: WBTC/WETH
10 2026-01-20T16:34 29.84
11 .
12 .
13 .
14 2026-02-01T17:50 33.46
15
16
172026-02-02T17:56 সময়ে WETH/USDC-এর মান কী হতে পারে বলে আপনি আশা করেন?
18
19আপনার উত্তরটি দুটি দশমিক স্থানে রাউন্ড করা একটি একক সংখ্যা হিসেবে দিন,
20অন্য কোনো লেখা ছাড়া।
সবকটি দেখুন

লক্ষ্য করুন যে এখানে দুটি অ্যাসেটের জন্য কোট রয়েছে, WETH/USDC এবং WBTC/WETH। অন্য একটি অ্যাসেট থেকে কোট যোগ করলে ভবিষ্যদ্বাণীর নির্ভুলতা উন্নত হতে পারে।

একটি প্রম্পট কেমন দেখতে হয়

এই প্রম্পটটিতে তিনটি বিভাগ রয়েছে, যা LLM প্রম্পটে বেশ সাধারণ।

  1. তথ্য। LLM-গুলির প্রশিক্ষণের অনেক তথ্য থাকে, কিন্তু সাধারণত তাদের কাছে সর্বশেষ তথ্য থাকে না। এই কারণে আমাদের এখানে সর্বশেষ কোটগুলি পুনরুদ্ধার করতে হবে। একটি প্রম্পটে তথ্য যোগ করাকে রিট্রিভাল অগমেন্টেড জেনারেশন (RAG) (opens in a new tab) বলা হয়।

  2. আসল প্রশ্ন। এটাই আমরা জানতে চাই।

  3. আউটপুট ফরম্যাটিং নির্দেশাবলী। সাধারণত, একটি LLM আমাদের একটি অনুমান দেয় এবং এটি কীভাবে সেই অনুমানে পৌঁছেছে তার একটি ব্যাখ্যা দেয়। এটি মানুষের জন্য ভালো, কিন্তু একটি কম্পিউটার প্রোগ্রামের শুধু চূড়ান্ত ফলাফল প্রয়োজন।

কোড ব্যাখ্যা

এখানে নতুন কোডটি দেওয়া হলো।

1from datetime import datetime, timezone, timedelta

আমাদের LLM-কে সেই সময়টি সরবরাহ করতে হবে যার জন্য আমরা একটি অনুমান চাই। ভবিষ্যতে "n মিনিট/ঘন্টা/দিন" সময় পেতে, আমরা timedelta ক্লাস (opens in a new tab) ব্যবহার করি।

1# আমরা যে পুলগুলো পড়ছি তার অ্যাড্রেস
2WETHUSDC_ADDRESS = Web3.to_checksum_address("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
3WETHWBTC_ADDRESS = Web3.to_checksum_address("0xCBCdF9626bC03E24f779434178A73a0B4bad62eD")

আমাদের দুটি পুল পড়তে হবে।

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, "Block is in the future"
10 sqrt_price_x96 = Decimal(self.contract.functions.slot0().call(block_identifier=block)[0])
11 raw_price = (sqrt_price_x96 / Decimal(2**96)) ** 2 # (টোকেন0 প্রতি টোকেন1)
12 if self.reverse:
13 return 1/(raw_price * self.decimal_factor)
14 else:
15 return raw_price * self.decimal_factor
সবকটি দেখুন

WETH/USDC পুলে, আমরা জানতে চাই একটি টোকেন1 (WETH) কিনতে কতগুলো টোকেন0 (USDC) প্রয়োজন। WETH/WBTC পুলে, আমরা জানতে চাই একটি টোকেন0 (WBTC, যা র‍্যাপড বিটকয়েন) কিনতে কতগুলো টোকেন1 (WETH) প্রয়োজন। আমাদের পুলের অনুপাত বিপরীত করা প্রয়োজন কিনা তা ট্র্যাক করতে হবে।

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 )
সবকটি দেখুন

একটি পুলকে বিপরীত করতে হবে কিনা তা জানতে, আমরা read_pool-এ ইনপুট হিসেবে তা পাই। এছাড়াও, অ্যাসেট প্রতীকটি সঠিকভাবে সেট করতে হবে।

<a> if <b> else <c> সিনট্যাক্সটি পাইথনে টারনারি কন্ডিশনাল অপারেটর (opens in a new tab) এর সমতুল্য, যা একটি C-ডিরাইভড ভাষায় <b> ? <a> : <c> হবে।

1def format_quotes(quotes: list[Quote]) -> str:
2 result = f"Asset: {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

এই ফাংশনটি Quote অবজেক্টের একটি তালিকা ফরম্যাট করে একটি স্ট্রিং তৈরি করে, ধরে নেওয়া হয় যে সবগুলো একই অ্যাসেটের জন্য প্রযোজ্য।

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

Python-এ মাল্টি-লাইন স্ট্রিং লিটারাল (opens in a new tab) লেখা হয় """ .... হিসেবে। """

1এই কোটগুলো দেওয়া আছে:
2{
3 functools.reduce(lambda acc, q: acc + '\n' + q,
4 map(lambda q: format_quotes(q), quotes))
5}

এখানে, আমরা প্রতিটি কোট তালিকার জন্য একটি স্ট্রিং তৈরি করতে MapReduce (opens in a new tab) প্যাটার্ন ব্যবহার করি, format_quotes দিয়ে, তারপর প্রম্পটে ব্যবহারের জন্য সেগুলোকে একটি একক স্ট্রিং-এ পরিণত করি।

1{expected_time} সময়ে {asset}-এর মান কী হবে বলে আপনি আশা করেন?
2
3আপনার উত্তরটি দুই দশমিক স্থান পর্যন্ত রাউন্ড করা একটি একক সংখ্যা হিসেবে দিন,
4অন্য কোনো লেখা ছাড়া।
5 """

প্রম্পটের বাকি অংশ প্রত্যাশিত।

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)
সবকটি দেখুন

দুটি পুল পর্যালোচনা করুন এবং উভয় থেকে কোট সংগ্রহ করুন।

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))

ভবিষ্যতের যে সময়বিন্দুর জন্য আমরা অনুমান চাই তা নির্ধারণ করুন এবং প্রম্পটটি তৈরি করুন।

একটি LLM-এর সাথে ইন্টারফেসিং

এরপরে, আমরা একটি আসল LLM-কে প্রম্পট করি এবং একটি প্রত্যাশিত ভবিষ্যৎ মান পাই। আমি এই প্রোগ্রামটি OpenAI ব্যবহার করে লিখেছি, তাই আপনি যদি অন্য কোনো প্রদানকারী ব্যবহার করতে চান, তাহলে আপনাকে এটি অ্যাডজাস্ট করতে হবে।

  1. একটি OpenAI অ্যাকাউন্ট (opens in a new tab) পান

  2. অ্যাকাউন্টে ফান্ড যোগ করুন (opens in a new tab)—লেখার সময় ন্যূনতম পরিমাণ হল $5

  3. একটি API কী তৈরি করুন (opens in a new tab)

  4. কমান্ড লাইনে, API কী এক্সপোর্ট করুন যাতে আপনার প্রোগ্রাম এটি ব্যবহার করতে পারে

    1export OPENAI_API_KEY=sk-<কী-এর বাকি অংশ এখানে দিন>
  5. এজেন্টটি চেকআউট করুন এবং চালান

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

এখানে নতুন কোডটি দেওয়া হলো।

1from openai import OpenAI
2
3open_ai = OpenAI() # ক্লায়েন্ট OPENAI_API_KEY এনভায়রনমেন্ট ভেরিয়েবল পড়ে

OpenAI API ইম্পোর্ট এবং ইনস্ট্যানশিয়েট করুন।

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)

প্রতিক্রিয়া তৈরি করতে OpenAI API (open_ai.chat.completions.create) কল করুন।

1expected_price = Decimal(response.choices[0].message.content.strip())
2current_price = wethusdc_quotes[-1].price
3
4print ("বর্তমান মূল্য:", wethusdc_quotes[-1].price)
5print(f"{future_time}-এ, প্রত্যাশিত মূল্য: {expected_price} USD")
6
7if (expected_price > current_price):
8 print(f"কিনুন, আমি আশা করি মূল্য {expected_price - current_price} USD বাড়বে")
9else:
10 print(f"বিক্রি করুন, আমি আশা করি মূল্য {current_price - expected_price} USD কমবে")
সবকটি দেখুন

মূল্যটি আউটপুট করুন এবং একটি কেনা বা বেচার সুপারিশ প্রদান করুন।

ভবিষ্যদ্বাণী পরীক্ষা করা

এখন যেহেতু আমরা ভবিষ্যদ্বাণী তৈরি করতে পারি, আমরা ঐতিহাসিক ডেটা ব্যবহার করে মূল্যায়ন করতে পারি যে আমরা দরকারী ভবিষ্যদ্বাণী তৈরি করছি কিনা।

1uv run test-predictor.py

প্রত্যাশিত ফলাফলটি অনেকটা এইরকম:

12026-01-05T19:50-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 3138.93 USD, আসল 3218.92 USD, ত্রুটি 79.99 USD
22026-01-06T19:56-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 3243.39 USD, আসল 3221.08 USD, ত্রুটি 22.31 USD
32026-01-07T20:02-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 3223.24 USD, আসল 3146.89 USD, ত্রুটি 76.35 USD
42026-01-08T20:11-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 3150.47 USD, আসল 3092.04 USD, ত্রুটি 58.43 USD
5.
6.
7.
82026-01-31T22:33-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 2637.73 USD, আসল 2417.77 USD, ত্রুটি 219.96 USD
92026-02-01T22:41-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 2381.70 USD, আসল 2318.84 USD, ত্রুটি 62.86 USD
102026-02-02T22:49-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী 2234.91 USD, আসল 2349.28 USD, ত্রুটি 114.37 USD
1129টি ভবিষ্যদ্বাণীর উপর গড় ভবিষ্যদ্বাণীর ত্রুটি: 83.87103448275862068965517241 USD
12প্রতি সুপারিশে গড় পরিবর্তন: 4.787931034482758620689655172 USD
13পরিবর্তনের স্ট্যান্ডার্ড ভ্যারিয়েন্স: 104.42 USD
14লাভজনক দিন: 51.72%
15লোকসানের দিন: 48.28%
সবকটি দেখুন

পরীক্ষকের বেশিরভাগ অংশ এজেন্টের সাথে অভিন্ন, কিন্তু এখানে নতুন বা পরিবর্তিত অংশগুলি রয়েছে।

1CYCLES_FOR_TEST = 40 # ব্যাকটেস্টের জন্য, আমরা কতগুলি চক্র পরীক্ষা করি
2
3# অনেক কোট পান
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)
সবকটি দেখুন

আমরা CYCLES_FOR_TEST (এখানে 40 হিসাবে নির্দিষ্ট) দিন পিছনে দেখি।

1# ভবিষ্যদ্বাণী তৈরি করুন এবং বাস্তব ইতিহাসের সাথে তাদের পরীক্ষা করুন
2
3total_error = Decimal(0)
4changes = []

দুই ধরনের ত্রুটি আছে যা নিয়ে আমরা আগ্রহী। প্রথমটি, total_error, হল ভবিষ্যদ্বাণীকারীর করা ত্রুটিগুলির সমষ্টি।

দ্বিতীয়টি, changes, বোঝার জন্য, আমাদের এজেন্টের উদ্দেশ্য মনে রাখতে হবে। এটি WETH/USDC অনুপাত (ETH মূল্য) ভবিষ্যদ্বাণী করার জন্য নয়। এটি বিক্রয় এবং কেনার সুপারিশ জারি করার জন্য। যদি বর্তমানে মূল্য $2000 হয় এবং এটি আগামীকাল $2010 ভবিষ্যদ্বাণী করে, তাহলে আসল ফলাফল যদি $2020 হয় এবং আমরা অতিরিক্ত অর্থ উপার্জন করি তবে আমরা কিছু মনে করি না। কিন্তু আমরা অবশ্যই কিছু মনে করি যদি এটি $2010 ভবিষ্যদ্বাণী করে, এবং সেই সুপারিশের ভিত্তিতে ETH কিনে, এবং দাম $1990-এ নেমে যায়।

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

আমরা কেবল সেইসব ক্ষেত্রে দেখতে পারি যেখানে সম্পূর্ণ ইতিহাস (ভবিষ্যদ্বাণীর জন্য ব্যবহৃত মান এবং এটির সাথে তুলনা করার জন্য বাস্তব-বিশ্বের মান) উপলব্ধ রয়েছে। এর মানে হল যে নতুনতম কেসটি অবশ্যই CYCLES_BACK আগে শুরু হয়েছে।

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

এজেন্ট যে সংখ্যক নমুনা ব্যবহার করে সেই একই সংখ্যক নমুনা পেতে স্লাইস (opens in a new tab) ব্যবহার করুন। এখানে এবং পরবর্তী সেগমেন্টের মধ্যে কোডটি এজেন্টে আমাদের থাকা একই get-a-prediction কোড।

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

ভবিষ্যদ্বাণী করা মূল্য, আসল মূল্য এবং ভবিষ্যদ্বাণীর সময়ের মূল্য পান। সুপারিশটি কেনার নাকি বেচার ছিল তা নির্ধারণ করার জন্য আমাদের ভবিষ্যদ্বাণীর সময়ের মূল্য প্রয়োজন।

1 error = abs(predicted_price - real_price)
2 total_error += error
3 print (f"{prediction_time}-এর জন্য ভবিষ্যদ্বাণী: ভবিষ্যদ্বাণী {predicted_price} USD, আসল {real_price} USD, ত্রুটি {error} USD")

ত্রুটি নির্ণয় করুন, এবং এটিকে মোটের সাথে যোগ করুন।

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)

changes-এর জন্য, আমরা এক ETH কেনা বা বেচার আর্থিক প্রভাব চাই। তাই প্রথমে, আমাদের সুপারিশটি নির্ধারণ করতে হবে, তারপর আসল মূল্য কীভাবে পরিবর্তিত হয়েছে তা মূল্যায়ন করতে হবে এবং সুপারিশটি লাভজনক ছিল (ধনাত্মক পরিবর্তন) নাকি লোকসানের কারণ হয়েছিল (ঋণাত্মক পরিবর্তন)।

1print (f"{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"প্রতি সুপারিশে গড় পরিবর্তন: {mean_change} USD")
6var = sum((x - mean_change) ** 2 for x in changes) / length_changes
7print (f"পরিবর্তনের স্ট্যান্ডার্ড ভ্যারিয়েন্স: {var.sqrt().quantize(Decimal("0.01"))} USD")

ফলাফলগুলি রিপোর্ট করুন।

1print (f"লাভজনক দিন: {len(list(filter(lambda x: x > 0, changes)))/length_changes:.2%}")
2print (f"লোকসানের দিন: {len(list(filter(lambda x: x < 0, changes)))/length_changes:.2%}")

লাভজনক দিন এবং লোকসানের দিনের সংখ্যা গণনা করতে filter (opens in a new tab) ব্যবহার করুন। ফলাফল একটি ফিল্টার অবজেক্ট, যা আমাদের দৈর্ঘ্য পেতে একটি তালিকায় রূপান্তর করতে হবে।

লেনদেন জমা দেওয়া

এখন আমাদের আসলে লেনদেন জমা দিতে হবে। তবে, সিস্টেমটি প্রমাণিত হওয়ার আগে আমি এই মুহূর্তে আসল টাকা খরচ করতে চাই না। পরিবর্তে, আমরা মেইননেটের একটি স্থানীয় ফর্ক তৈরি করব এবং সেই নেটওয়ার্কে "ট্রেড" করব।

এখানে একটি স্থানীয় ফর্ক তৈরি এবং ট্রেডিং সক্ষম করার ধাপগুলি দেওয়া হল।

  1. Foundry (opens in a new tab) ইনস্টল করুন

  2. anvil (opens in a new tab) শুরু করুন

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

    anvil Foundry-এর ডিফল্ট URL, http://localhost:8545-এ (opens in a new tab) লিসেন করছে, তাই ব্লকচেইন ম্যানিপুলেট করার জন্য আমরা যে cast কমান্ড (opens in a new tab) ব্যবহার করি তার জন্য URL নির্দিষ্ট করার প্রয়োজন নেই।

  3. anvil-এ চালানোর সময়, দশটি টেস্ট অ্যাকাউন্ট থাকে যেগুলোতে ETH আছে—প্রথমটির জন্য এনভায়রনমেন্ট ভেরিয়েবল সেট করুন

    1PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
    2ADDRESS=`cast wallet address $PRIVATE_KEY`
  4. এগুলো হল কন্ট্র্যাক্ট যা আমাদের ব্যবহার করতে হবে। SwapRouter (opens in a new tab) হল Uniswap v3 কন্ট্র্যাক্ট যা আমরা আসলে ট্রেড করতে ব্যবহার করি। আমরা সরাসরি পুলের মাধ্যমে ট্রেড করতে পারতাম, কিন্তু এটি অনেক সহজ।

    নীচের দুটি ভেরিয়েবল হল WETH এবং USDC-এর মধ্যে সোয়াপ করার জন্য প্রয়োজনীয় Uniswap v3 পাথ।

    1WETH_ADDRESS=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    2USDC_ADDRESS=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    3POOL_ADDRESS=0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640
    4SWAP_ROUTER=0xE592427A0AEce92De3Edee1F18E0157C05861564
    5WETH_TO_USDC=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc20001F4A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    6USDC_TO_WETH=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480001F4C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
  5. প্রতিটি টেস্ট অ্যাকাউন্টে 10,000 ETH আছে। ট্রেডিংয়ের জন্য 1000 WETH পেতে 1000 ETH র‍্যাপ করতে WETH কন্ট্র্যাক্ট ব্যবহার করুন।

    1cast send $WETH_ADDRESS "deposit()" --value 1000ether --private-key $PRIVATE_KEY
  6. SwapRouter ব্যবহার করে 500 WETH ট্রেড করে 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

    approve কল একটি অ্যালাওয়েন্স তৈরি করে যা SwapRouter-কে আমাদের কিছু টোকেন খরচ করার অনুমতি দেয়। কন্ট্র্যাক্টগুলি ইভেন্টগুলি নিরীক্ষণ করতে পারে না, তাই যদি আমরা সরাসরি SwapRouter কন্ট্র্যাক্টে টোকেন স্থানান্তর করি, তবে এটি জানতে পারবে না যে এটি পরিশোধ করা হয়েছে। পরিবর্তে, আমরা SwapRouter কন্ট্র্যাক্টকে একটি নির্দিষ্ট পরিমাণ খরচ করার অনুমতি দিই, এবং তারপর SwapRouter এটি করে। এটি SwapRouter দ্বারা কল করা একটি ফাংশনের মাধ্যমে করা হয়, তাই এটি জানে যে এটি সফল হয়েছে কিনা।

  7. আপনার কাছে উভয় টোকেন পর্যাপ্ত পরিমাণে আছে কিনা তা যাচাই করুন।

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

এখন যেহেতু আমাদের কাছে WETH এবং USDC আছে, আমরা আসলে এজেন্টটি চালাতে পারি।

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

আউটপুটটি দেখতে অনেকটা এইরকম হবে:

1(ai-trading-agent) qbzzt@Ori-Cloudnomics:~/260215-ai-agent$ uv run agent.py
2বর্তমান মূল্য: 1843.16
32026-02-06T23:07-এ, প্রত্যাশিত মূল্য: 1724.41 USD
4ট্রেডের আগে অ্যাকাউন্টের ব্যালেন্স:
5USDC ব্যালেন্স: 927301.578272
6WETH ব্যালেন্স: 500
7বিক্রি করুন, আমি আশা করি মূল্য 118.75 USD কমবে
8অনুমোদন লেনদেন পাঠানো হয়েছে: 74e367ddbb407c1aaf567d87aa5863049991b1d2aa092b6b85195d925e2bd41f
9অনুমোদন লেনদেন মাইনিং করা হয়েছে।
10বিক্রয় লেনদেন পাঠানো হয়েছে: fad1bcf938585c9e90364b26ac7a80eea9efd34c37e5db81e58d7655bcae28bf
11বিক্রয় লেনদেন মাইনিং করা হয়েছে।
12ট্রেডের পরে অ্যাকাউন্টের ব্যালেন্স:
13USDC ব্যালেন্স: 929143.797116
14WETH ব্যালেন্স: 499
সবকটি দেখুন

এটি আসলে ব্যবহার করার জন্য, আপনাকে কয়েকটি ছোট পরিবর্তন করতে হবে।

  • লাইন 14-এ, MAINNET_URL পরিবর্তন করে একটি বাস্তব অ্যাক্সেস পয়েন্ট, যেমন https://eth.drpc.org-এ দিন
  • লাইন 28-এ, PRIVATE_KEY পরিবর্তন করে আপনার নিজের প্রাইভেট কী দিন
  • যদি না আপনি খুব ধনী হন এবং একটি অপ্রমাণিত এজেন্টের জন্য প্রতিদিন 1 ETH কিনতে বা বিক্রি করতে পারেন, তাহলে আপনি WETH_TRADE_AMOUNT কমাতে 29 পরিবর্তন করতে চাইতে পারেন

কোড ব্যাখ্যা

এখানে নতুন কোডটি দেওয়া হলো।

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

ধাপ 4-এ আমরা যে একই ভেরিয়েবল ব্যবহার করেছি।

1WETH_TRADE_AMOUNT=1

ট্রেড করার পরিমাণ।

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

আসলে ট্রেড করার জন্য, আমাদের approve ফাংশন প্রয়োজন। আমরা আগে এবং পরে ব্যালেন্সও দেখাতে চাই, তাই আমাদের balanceOf ও প্রয়োজন।

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

SwapRouter ABI-তে আমাদের শুধু exactInput প্রয়োজন। একটি সম্পর্কিত ফাংশন আছে, exactOutput, যা আমরা ঠিক একটি WETH কেনার জন্য ব্যবহার করতে পারতাম, কিন্তু সরলতার জন্য আমরা উভয় ক্ষেত্রে exactInput ব্যবহার করি।

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

account (opens in a new tab) এবং SwapRouter কন্ট্র্যাক্টের জন্য Web3 সংজ্ঞা।

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 }

লেনদেনের প্যারামিটার। এখানে আমাদের একটি ফাংশন প্রয়োজন কারণ নন্স (opens in a new tab) প্রতিবার পরিবর্তন হতে হবে।

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

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)

এভাবে আমরা Web3-এ একটি লেনদেন পাঠাই। প্রথমে আমরা লেনদেন তৈরি করতে Contract অবজেক্ট (opens in a new tab) ব্যবহার করি। তারপর আমরা PRIVATE_KEY ব্যবহার করে লেনদেন সাইন করতে web3.eth.account.sign_transaction (opens in a new tab) ব্যবহার করি। অবশেষে, আমরা লেনদেন পাঠাতে w3.eth.send_raw_transaction (opens in a new tab) ব্যবহার করি।

1 print(f"অনুমোদন লেনদেন পাঠানো হয়েছে: {tx_hash.hex()}")
2 w3.eth.wait_for_transaction_receipt(tx_hash)
3 print("অনুমোদন লেনদেন মাইনিং করা হয়েছে।")

w3.eth.wait_for_transaction_receipt (opens in a new tab) লেনদেন মাইনিং না হওয়া পর্যন্ত অপেক্ষা করে। প্রয়োজন হলে এটি রসিদ প্রদান করে।

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}

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 }

SELL_PARAMS-এর বিপরীতে, কেনার প্যারামিটারগুলি পরিবর্তন হতে পারে। ইনপুট পরিমাণ হল 1 WETH-এর খরচ, যা 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"কেনার লেনদেন পাঠানো হয়েছে: {tx_hash.hex()}")
8 w3.eth.wait_for_transaction_receipt(tx_hash)
9 print("কেনার লেনদেন মাইনিং করা হয়েছে।")
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"বিক্রয় লেনদেন পাঠানো হয়েছে: {tx_hash.hex()}")
19 w3.eth.wait_for_transaction_receipt(tx_hash)
20 print("বিক্রয় লেনদেন মাইনিং করা হয়েছে।")
সবকটি দেখুন

buy() এবং sell() ফাংশনগুলি প্রায় অভিন্ন। প্রথমে আমরা SwapRouter-এর জন্য একটি পর্যাপ্ত অ্যালাওয়েন্স অনুমোদন করি এবং তারপর আমরা সঠিক পাথ এবং পরিমাণ দিয়ে এটি কল করি।

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} ব্যালেন্স: {Decimal(token0_balance) / Decimal(10 ** wethusdc_pool.token0.decimals)}")
6 print(f"{wethusdc_pool.token1.symbol} ব্যালেন্স: {Decimal(token1_balance) / Decimal(10 ** wethusdc_pool.token1.decimals)}")

উভয় মুদ্রায় ব্যবহারকারীর ব্যালেন্স রিপোর্ট করুন।

1print("ট্রেডের আগে অ্যাকাউন্টের ব্যালেন্স:")
2balances()
3
4if (expected_price > current_price):
5 print(f"কিনুন, আমি আশা করি মূল্য {expected_price - current_price} USD বাড়বে")
6 buy(wethusdc_quotes[-1])
7else:
8 print(f"বিক্রি করুন, আমি আশা করি মূল্য {current_price - expected_price} USD কমবে")
9 sell()
10
11print("ট্রেডের পরে অ্যাকাউন্টের ব্যালেন্স:")
12balances()
সবকটি দেখুন

এই এজেন্ট বর্তমানে শুধুমাত্র একবার কাজ করে। তবে, আপনি এটিকে crontab (opens in a new tab) থেকে চালিয়ে বা একটি লুপে 368-400 লাইনগুলি র‍্যাপ করে এবং পরবর্তী চক্রের জন্য অপেক্ষা করার জন্য time.sleep (opens in a new tab) ব্যবহার করে ক্রমাগত কাজ করার জন্য পরিবর্তন করতে পারেন।

সম্ভাব্য উন্নতি

এটি একটি সম্পূর্ণ প্রোডাকশন সংস্করণ নয়; এটি শুধুমাত্র মূল বিষয়গুলি শেখানোর জন্য একটি উদাহরণ। এখানে উন্নতির জন্য কিছু ধারণা দেওয়া হল।

স্মার্ট ট্রেডিং

দুটি গুরুত্বপূর্ণ তথ্য রয়েছে যা এজেন্ট কী করতে হবে তা সিদ্ধান্ত নেওয়ার সময় উপেক্ষা করে।

  • প্রত্যাশিত পরিবর্তনের মাত্রা। মূল্য হ্রাসের মাত্রা নির্বিশেষে, যদি মূল্য হ্রাসের প্রত্যাশা করা হয় তবে এজেন্ট একটি নির্দিষ্ট পরিমাণ WETH বিক্রি করে। যুক্তিযুক্তভাবে, ছোটখাটো পরিবর্তন উপেক্ষা করা এবং আমরা কতটা মূল্য হ্রাসের প্রত্যাশা করি তার উপর ভিত্তি করে বিক্রি করা ভাল হবে।
  • বর্তমান পোর্টফোলিও। যদি আপনার পোর্টফোলিওর 10% WETH-এ থাকে এবং আপনি মনে করেন দাম বাড়বে, তাহলে সম্ভবত আরও কেনা যুক্তিযুক্ত। কিন্তু যদি আপনার পোর্টফোলিওর 90% WETH-এ থাকে, তাহলে আপনি যথেষ্ট এক্সপোজড হতে পারেন, এবং আরও কেনার প্রয়োজন নেই। আপনি যদি দাম কমার আশা করেন তবে এর বিপরীতটি সত্য।

আপনি যদি আপনার ট্রেডিং কৌশল গোপন রাখতে চান তাহলে কী হবে?

AI বিক্রেতারা তাদের LLM-গুলিতে আপনার পাঠানো কোয়েরিগুলি দেখতে পারে, যা আপনার এজেন্টের সাথে আপনি যে জিনিয়াস ট্রেডিং সিস্টেম তৈরি করেছেন তা প্রকাশ করতে পারে। একটি ট্রেডিং সিস্টেম যা অনেক লোক ব্যবহার করে তা মূল্যহীন কারণ যখন আপনি কিনতে চান তখন অনেক লোক কেনার চেষ্টা করে (এবং দাম বেড়ে যায়) এবং যখন আপনি বিক্রি করতে চান তখন বিক্রি করার চেষ্টা করে (এবং দাম কমে যায়)।

এই সমস্যা এড়াতে আপনি স্থানীয়ভাবে একটি LLM চালাতে পারেন, উদাহরণস্বরূপ, LM-Studio (opens in a new tab) ব্যবহার করে।

AI বট থেকে AI এজেন্ট

আপনি একটি ভাল যুক্তি দিতে পারেন যে এটি একটি AI বট, AI এজেন্ট নয়। এটি একটি তুলনামূলকভাবে সহজ কৌশল বাস্তবায়ন করে যা পূর্বনির্ধারিত তথ্যের উপর নির্ভর করে। আমরা স্ব-উন্নতি সক্ষম করতে পারি, উদাহরণস্বরূপ, Uniswap v3 পুল এবং তাদের সর্বশেষ মানগুলির একটি তালিকা প্রদান করে এবং জিজ্ঞাসা করে কোন সংমিশ্রণের সেরা ভবিষ্যদ্বাণীমূলক মান রয়েছে।

স্লিপেজ সুরক্ষা

বর্তমানে কোনো স্লিপেজ সুরক্ষা (opens in a new tab) নেই। যদি বর্তমান কোট $2000 হয় এবং প্রত্যাশিত মূল্য $2100 হয়, এজেন্ট কিনবে। তবে, এজেন্ট কেনার আগে যদি খরচ $2200-এ বেড়ে যায়, তবে আর কেনার কোনো মানে হয় না।

স্লিপেজ সুরক্ষা বাস্তবায়ন করতে, agent.py (opens in a new tab)-এর 325 এবং 334 লাইনে একটি amountOutMinimum মান নির্দিষ্ট করুন।

উপসংহার

আশা করি, এখন আপনি AI এজেন্টদের সাথে শুরু করার জন্য যথেষ্ট জানেন। এটি বিষয়টির একটি ব্যাপক ওভারভিউ নয়; এর জন্য পুরো বই উৎসর্গ করা হয়েছে, তবে এটি আপনাকে শুরু করার জন্য যথেষ্ট। শুভকামনা!

আমার আরও কাজের জন্য এখানে দেখুন (opens in a new tab)

পৃষ্ঠাটি সর্বশেষ আপডেট করা হয়েছে: ১০ ফেব্রুয়ারী, ২০২৬

এই টিউটোরিয়ালটি কি সহায়ক ছিল?