মূল কনটেন্টে যান
এআই টিউটোরিয়াল

jev-trader দিয়ে ব্লক-বাই-ব্লক Monad মার্কেট মেকার তৈরি ও চালান

jev-trader কীভাবে ইনস্টল করতে হয়, নিরাপদে dry-run মোডে চালাতে হয়, Jev মডেল সংযুক্ত করতে হয়, এর HTTP ও SSE ফিড পরিদর্শন করতে হয়, কোট ও ফিলের ব্যাখ্যা বুঝতে হয় এবং Monad-এর প্রায় 300 ms ব্লক সময়ের মধ্যে ট্রেডিং লুপ বজায় রাখা ল্যাটেন্সি অপ্টিমাইজেশনগুলো সম্পর্কে জানুন।

jev-trader দিয়ে ব্লক-বাই-ব্লক Monad মার্কেট মেকার তৈরি ও চালান

jev-trader কী করে

jev-trader হলো Monad-এর Kuru MON-USDC অর্ডার বইয়ের জন্য ব্লক-ভিত্তিক ট্রেডিং বট। প্রতিটি নতুন ব্লকে এটি লাইভ অর্ডার বই পড়ে, কেনা বা বিক্রি করার বিষয়ে একটি মডেলের মতামত নেয় এবং নির্বাচিত পাশে একটি post-only limit-order intent তৈরি করে।

অর্ডারটির মূল্য বর্তমান টাচের ভেতরে QUOTE_INSIDE_TICKS টিক নির্ধারণ করা হয়। স্প্রেড খুব সংকীর্ণ হলে মূল্যটি টাচে সীমাবদ্ধ থাকে। বটটি আগে থেকে resting থাকা অর্ডার বাতিল করে এবং একটি Kuru batchUpdate-এর মাধ্যমে প্রতিস্থাপনকারী অর্ডার পোস্ট করে।

অর্ডারটি post-only হওয়ায় তা অবিলম্বে কার্যকর হওয়ার বদলে বইয়ে resting অবস্থায় থাকার জন্য নির্ধারিত। অন্য কোনো অংশগ্রহণকারী তার মূল্য অতিক্রম করে taker order জমা দিলে fill ঘটে। এর ফলে কৌশলটি স্প্রেড প্রদান না করে তা আয় করতে পারে, যদিও execution, inventory, gas এবং বাজারের ঝুঁকি প্রযোজ্য থাকে।

প্রধান বৈশিষ্ট্য

  • Monad-এর প্রতি ব্লকে একটি সিদ্ধান্ত: বটটি প্রায় প্রতি ৩০০ ms-এ সর্বশেষ ব্লক প্রক্রিয়া করে।
  • লাইভ Kuru অর্ডার-বুক ডেটা: একটি কাস্টম reader getL2Book ডিকোড করে এবং AMM vault-কে বইয়ের সঙ্গে একীভূত করে।
  • দুটি মডেল বিকল্প: ডিফল্ট mock model একটি momentum heuristic ব্যবহার করে, আর MODEL=jev TypeSafe Jev model সক্রিয় করে।
  • নিরাপদ dry-run আচরণ: PRIVATE_KEY না থাকলে বটটি বাস্তব বই ও বাস্তব market prints ব্যবহার করে, কিন্তু নিজের অর্ডার ও fill সিমুলেট করে।
  • Post-only অর্ডার প্রতিস্থাপন: প্রতিটি আপডেটে পরিচিত resting অর্ডার বাতিল করে একটি batch-এর মাধ্যমে নতুন post-only অর্ডার জমা দেয়।
  • Position ও P&L ট্র্যাকিং: ইভেন্টগুলো position, realized ও unrealized ফলাফল, gas খরচ, fill-এর সংখ্যা এবং অন্যান্য মোট হিসাব জানায়।
  • HTTP ও SSE মনিটরিং: একটি ছোট Bun server সর্বশেষ snapshot, সর্বোচ্চ ১,০০০টি ঐতিহাসিক block event এবং একটি live event stream প্রকাশ করে।
  • অ্যাসিনক্রোনাস confirmation: live transaction-গুলো hot path-এ জমা দেওয়া হয়, আর receipt ও সংশ্লিষ্ট পরীক্ষা পরে সম্পন্ন হয়।

প্রজেক্টের আর্কিটেকচার

Source tree-তে block ingestion, market access, model evaluation, trading logic এবং monitoring আলাদা রাখা হয়েছে:

  • src/config.ts environment configuration লোড করে।
  • src/chain.ts WebSocket newHeads আপডেট গ্রহণ করে, backstop হিসেবে polling ব্যবহার করে এবং কেবল সর্বশেষ ব্লকটি রাখে।
  • src/book.ts একটি eth_call-এর মাধ্যমে অর্ডার বই পড়ে ও ডিকোড করে।
  • src/market.ts Kuru book read, margin deposit, local nonce, batch update এবং asynchronous confirmation পরিচালনা করে।
  • src/model.ts model interface নির্ধারণ করে এবং JevModel ও MockModel সরবরাহ করে।
  • src/trader.ts trading loop চালায় এবং position ও P&L ট্র্যাক করে।
  • src/server.ts Bun.serve ব্যবহার করে snapshot, history এবং Server-Sent Events পরিবেশন করে।

ইনস্টলেশন ও সেটআপ

১. Environment file প্রস্তুত করুন

Repository directory থেকে উদাহরণ configuration কপি করুন:

cp .env.example .env

অ্যাপ্লিকেশন শুরু করার আগে তৈরি হওয়া .env ফাইলটি পর্যালোচনা করুন। সবচেয়ে গুরুত্বপূর্ণ আচরণ PRIVATE_KEY উপস্থিত আছে কি না, তার ওপর নির্ভর করে।

প্রথমবার চালানোর সময় PRIVATE_KEY সেট করবেন না। অ্যাপ্লিকেশন নিজের অর্ডার broadcast না করে বাস্তব market data-এর ওপর dry-run করবে।

২. Dependencies ইনস্টল করুন

প্রজেক্টটি dependency management ও execution-এর জন্য Bun ব্যবহার করে:

bun install

৩. Trader শুরু করুন

bun run start

ডিফল্ট configuration-এ mock momentum model একটি inference stand-in হিসেবে কাজ করে। এরপর server তার HTTP endpoint-গুলোর মাধ্যমে trader state ও event stream উপলব্ধ করে।

Decision model নির্বাচন

ডিফল্ট mock model ব্যবহার করুন

Jev-কে কল না করেই সম্পূর্ণ pipeline পরীক্ষা করার জন্য ডিফল্ট mock model উপযোগী। এটি momentum heuristic ব্যবহার করে সিদ্ধান্ত নেয় এবং book read, simulated order, fill, accounting ও event streaming যাচাই করার সুযোগ দেয়।

Jev model সক্রিয় করুন

Jev ব্যবহার করতে .env-এ এই মানগুলো configure করুন:

MODEL=jev
TYPESAFE_AI_API_KEY=your_api_key

প্রতিটি ব্লকে নির্বাচিত model-কে HORIZON_BLOCKS-এর সময়সীমায় প্রত্যাশিত পরিবর্তন সম্পর্কে জিজ্ঞাসা করা হয়। এর ডিফল্ট মান ১০০ ব্লক, যা প্রজেক্ট README অনুযায়ী প্রায় ৩০ সেকেন্ড।

স্বাভাবিক সিদ্ধান্ত হয় buy অথবা sell। hold ফলাফল তৃতীয় কোনো model strategy নয়: model বর্তমান block deadline মিস করলেই এটি দেখা যায়। সে ক্ষেত্রে decision.late হলো true এবং ওই ব্লকে কোনো অর্ডার পোস্ট করা হয় না।

Dry-run mode-এ নিরাপদে চালানো

PRIVATE_KEY configure করা না থাকলে jev-trader live transaction জমা দেয় না। তবুও এটি বাস্তব Kuru book পড়ে এবং বাস্তব model decision তৈরি করে।

একটি simulated quote-এর status: "sim" থাকে। এটি এক ব্লক resting থাকে এবং কেবল কোনো বাস্তব market print তার মূল্য অতিক্রম করলেই filled হিসেবে গণ্য হয়। ফলস্বরূপ fill-এ simulated: true থাকে।

এই mode-টি যাচাই করার জন্য উপযোগী:

  • Book update নির্ভরযোগ্যভাবে আসছে কি না।
  • Model latency block budget-এর মধ্যে থাকছে কি না।
  • কত ঘন ঘন quote filled হওয়ার সম্ভাবনা রয়েছে।
  • Simulation rule-এর অধীনে position ও P&L কীভাবে পরিবর্তিত হয়।
  • Dashboard বা অন্য কোনো SSE client প্রতিটি event type সঠিকভাবে পরিচালনা করছে কি না।

HTTP endpoint পরিদর্শন

Mock model ব্যবহারকারী একটি deployed dry-run instance এখানে উপলব্ধ: https://jev-trader-production.up.railway.app।

সর্বশেষ স্ন্যাপশট দেখুন

রুট এন্ডপয়েন্ট সক্রিয় মডেল, ওয়ালেট, ড্রাই-রান অবস্থা এবং সর্বশেষ ব্লক ইভেন্ট জানায়:

curl https://jev-trader-production.up.railway.app/

সাম্প্রতিক ইতিহাস দেখুন

হিস্ট্রি এন্ডপয়েন্ট সর্বশেষ 1,000টি ব্লক ইভেন্ট ফেরত দেয়:

curl https://jev-trader-production.up.railway.app/history

লাইভ SSE স্ট্রিম অনুসরণ করুন

সংযোগ খোলা রাখতে এবং ইভেন্ট আসার সঙ্গে সঙ্গে দেখাতে curl -N ব্যবহার করুন:

curl -N https://jev-trader-production.up.railway.app/events

ক্লায়েন্ট সংযুক্ত হলে স্ট্রিম একটি snapshot ইভেন্ট পাঠায়, এরপর প্রক্রিয়াকৃত প্রতিটি ব্লকের জন্য একটি করে block ইভেন্ট পাঠায়। লাইভ ট্রানজ্যাকশন রসিদে quote আপডেট তৈরি হয় এবং শনাক্ত করা এক্সিকিউশনে fill ইভেন্ট তৈরি হয়।

একটি ব্লক ইভেন্ট বোঝা

একটি ব্লক ইভেন্টে বাজারের অবস্থা, মডেলের সিদ্ধান্ত, নতুন কোটের অভিপ্রায়, অপেক্ষমাণ লিকুইডিটি, পজিশনের তথ্য এবং ক্রমবর্ধমান মোট হিসাব একত্রে থাকে। সংক্ষিপ্ত উদাহরণটি এমন:

{
  "block": 105488269,
  "mid": 0.022636,
  "bestBid": 0.022628,
  "bestAsk": 0.022644,
  "spreadBps": 7.07,
  "decision": {
    "action": "buy",
    "probabilities": { "buy": 0.77, "sell": 0.23, "hold": 0 },
    "upIn10": 0.77,
    "latencyMs": 81,
    "late": false
  },
  "quote": {
    "side": "buy",
    "price": 0.022629,
    "size": 200,
    "status": "sent",
    "capped": false
  }
}
  • mid, bestBid এবং bestAsk পর্যবেক্ষণ করা বাজারের বর্ণনা দেয়।
  • spreadBps বর্তমান স্প্রেডকে বেসিস পয়েন্টে প্রকাশ করে।
  • decision.action মডেল নির্বাচিত দিক নির্দেশ করে।
  • decision.probabilities মডেলের buy এবং sell সম্ভাবনা অপরিবর্তিত রাখে।
  • upIn10 buy সম্ভাবনার সমান।
  • decision.latencyMs মডেল মূল্যায়নের বিলম্ব জানায়।
  • quote এই ব্লকের জন্য চেষ্টা করা অর্ডারের বর্ণনা দেয়।
  • quote.cancel বাতিলের জন্য অন্তর্ভুক্ত আগে থেকে অপেক্ষমাণ অর্ডার শনাক্ত করে।
  • resting আপডেটের পর বটের জানা bid এবং ask-এর পরিমাণ নথিবদ্ধ করে।
  • position দিক, পরিমাণ, প্রবেশমূল্য এবং অবাস্তবায়িত ফলাফল জানায়।
  • totals ব্লক, সিদ্ধান্ত, কোট, ফিল, রিভার্শন, বিলম্বিত ব্লক, মডেল খরচ, গ্যাস এবং P&L-এর ক্রমবর্ধমান হিসাব রাখে।

পজিশন ও ফান্ডিং সীমা

পজিশন সীমা অনুরোধ করা দিকটি আটকে দিলে, অথবা লাইভ মার্জিন তহবিল এর জন্য অপর্যাপ্ত হলে, কোটটি বিপরীত দিকে স্থাপন করা হয়। এ ক্ষেত্রে quote.capped হলো true। মূল মডেল সম্ভাবনাগুলো অপরিবর্তিত থাকে, ফলে ব্যবহারকারীরা মডেলের সিদ্ধান্ত এবং প্রকৃতপক্ষে কোট করা দিক আলাদা করতে পারেন।

লাইভ কোট নিশ্চিতকরণ বোঝা

লাইভ পাঠানোর কাজ সম্পন্ন হয়েছে কি না তার জন্য অপেক্ষা করা হয় না, যাতে ট্রেডিং লুপ রসিদের অপেক্ষায় না থাকে। তাই প্রাথমিক ব্লক ইভেন্টে status: "sent" সহ একটি অভিপ্রায় থাকে।

প্রতিবেদিত gasMon গ্যাস লিমিটকে সর্বশেষ জানা বেস ফি এবং প্রায়োরিটি ফি দিয়ে গুণ করে গণনা করা হয়। প্রকল্পের নোট অনুযায়ী Monad গ্যাস লিমিট অনুযায়ী চার্জ করে, তাই ট্রানজ্যাকশন সফল হোক বা রিভার্ট হোক, এটিকে প্রকৃত খরচ হিসেবে ধরা হয়।

সাধারণত এক বা দুই ব্লক পরে একটি রসিদ আসে এবং আলাদা একটি quote ইভেন্ট তৈরি হয়:

event: quote
data: {
  "block": 105488269,
  "quote": {
    "status": "placed",
    "orderId": 100295812,
    "gasMon": 0.0357
  }
}

একটি লাইভ কোট নিচের যেকোনো একটি অবস্থায় যেতে পারে:

  • placed: ট্রানজ্যাকশন সফল হয়েছে এবং এতে একটি অর্ডার আইডি রয়েছে।
  • reverted: অন্তর্ভুক্তির আগে বুকটি জমা দেওয়া মূল্যের মধ্য দিয়ে চলে গেছে, অথবা অনুরোধ করা বাতিলকরণ এমন একটি অর্ডারকে লক্ষ্য করেছে যা ইতিমধ্যে পূরণ হয়ে গেছে।
  • lost: 10টি ব্লকের পর কোনো রসিদ পাওয়া যায়নি।

ফিল ইভেন্ট বোঝা

বটের নিজস্ব প্লেসমেন্ট ট্রানজ্যাকশনে ফিল ঘটে না। অন্য কোনো ট্রেডারের taker অর্ডার অপেক্ষমাণ অর্ডারে আঘাত করলে ফিল ঘটে। মডেলকে বাজারের তথ্য সরবরাহ করা একই eth_getLogs পোলিং প্রক্রিয়ার মাধ্যমে বট সংশ্লিষ্ট Trade লগ শনাক্ত করে।

event: fill
data: {
  "block": 105488271,
  "fill": {
    "side": "buy",
    "size": 200,
    "price": 0.022629,
    "txHash": "0x…",
    "orderId": 100295812,
    "simulated": false
  }
}

ফিলের txHash taker ট্রানজ্যাকশনের। কোনো ফিল প্রক্রিয়াকৃত হলে, স্ট্রিমে আপডেট করা পজিশন তথ্য, বাস্তবায়িত P&L এবং ক্রমবর্ধমান ফিলের মোট হিসাবও প্রতিফলিত হয়।

লাইভ ট্রেডিংয়ে যাওয়া

PRIVATE_KEY সেট করলে অ্যাপ্লিকেশন ড্রাই-রান আচরণ থেকে লাইভ ট্রানজ্যাকশন জমা দেওয়ার মোডে যায়। এটি করার আগে পরিবেশ কনফিগারেশন, মার্জিন ফান্ডিং, পজিশন সীমা, ট্রেডের পরিমাণ, কোট প্লেসমেন্ট, ফি সীমা এবং RPC নির্ভরযোগ্যতা যাচাই করুন।

লাইভ মোড প্রকৃত post-only Kuru অর্ডার জমা দেয় এবং প্রকৃত গ্যাস খরচ হয়। প্রথমে ড্রাই-রান মোডে সম্পূর্ণ ওয়ার্কফ্লো পরীক্ষা করুন এবং ফান্ডযুক্ত কী সক্রিয় করার সময় সতর্কতামূলক সীমা ব্যবহার করুন।

প্রকল্পে বর্ণিত প্রধান কৌশল-সম্পর্কিত সেটিংগুলো হলো:

  • অর্ডারের পরিমাণের জন্য TRADE_SIZE_MON।
  • টাচের তুলনায় প্লেসমেন্টের জন্য QUOTE_INSIDE_TICKS।
  • মডেলের পূর্বাভাসের সময়সীমার জন্য HORIZON_BLOCKS।
  • টাইপ-2 ট্রানজ্যাকশন ফি সীমার জন্য MAX_FEE_GWEI।
  • অর্ডার-বুক পড়ার জন্য READ_RPC_URL।
  • ট্রানজ্যাকশন জমা দেওয়ার জন্য RPC_URL।

বট কীভাবে 300 ms সীমার মধ্যে থাকে

হট পাথটি ঠিক দুটি RPC রাউন্ড ট্রিপকে কেন্দ্র করে তৈরি:

  1. একটি eth_call READ_RPC_URL-এর মাধ্যমে বর্তমান অর্ডার বুক পড়ে।
  2. একটি eth_sendRawTransaction স্বাক্ষর করা আপডেটটি RPC_URL-এর মাধ্যমে জমা দেয় এবং গ্রহণ হওয়া পর্যন্ত ফেরত আসে।

লুপটি ইচ্ছাকৃতভাবে সম্ভাব্য ব্যয়বহুল কয়েকটি অপারেশন এড়িয়ে চলে:

  • প্রতি ব্লকের জন্য কোনো eth_estimateGas নেই; গ্যাস লিমিট হার্ডকোড করা থাকে অথবা স্টার্টআপের সময় একবার নির্ধারণ করা হয়।
  • কোনো eth_sendRawTransactionSync নেই, যা ট্রানজ্যাকশন প্রস্তাবিত হওয়া পর্যন্ত অপেক্ষা করে।
  • হট পাথে গ্যাস-দামের কোনো লুকআপ নেই; ট্রানজ্যাকশনগুলো 2 gwei প্রায়োরিটি ফি এবং MAX_FEE_GWEI ক্যাপসহ স্ট্যাটিক টাইপ-2 ফি সেটিংস ব্যবহার করে।
  • সিদ্ধান্ত লুপে রসিদ পাওয়ার জন্য কোনো অপেক্ষা নেই।
  • অর্ডারে বিলম্ব ঘটাতে পারে এমন কোনো ইনলাইন ভল্ট বা ফি যাচাই নেই।

README-তে ড্রাই-রান মক-মডেল বেঞ্চমার্ক অনুযায়ী বুক পড়ার p50 সময় প্রায় 18 ms এবং পুরো লুপের p50 সময় 100 ms; এর মধ্যে প্রায় 80 ms মক ইনফারেন্স স্ট্যান্ড-ইনের জন্য দায়ী।

ভ্যালিডেশন ও বেঞ্চমার্কিং

কাস্টম বুক রিডার বেঞ্চমার্ক করুন

নির্ভুলতা ও লেটেন্সির তুলনার জন্য SDK-এর সঙ্গে কাস্টম রিডারটি তুলনা করতে অন্তর্ভুক্ত বেঞ্চমার্ক চালান:

bun run scripts/bench-read.ts

অফলাইনে ট্রানজ্যাকশন এনকোডিং যাচাই করুন

ড্রাই এনকোডিং স্ক্রিপ্ট কোনো ব্রডকাস্ট ছাড়াই একটি বাই ও একটি সেল সাইন করে, তারপর তৈরি হওয়া calldata SDK-এর সঙ্গে মেলে কি না তা যাচাই করে:

bun run scripts/dry-encode.ts

লাইভ মোড সক্রিয় করার আগে এটি বিশেষভাবে কার্যকর, কারণ পারফরম্যান্স-কেন্দ্রিক মার্কেট মডিউলটি হাতে এনকোড করা batchUpdate calldata ব্যবহার করে।

উন্নত পরিচালনাগত পরামর্শ

  • দেরিতে আসা সিদ্ধান্ত পর্যবেক্ষণ করুন: decision.late এবং totals.lateBlocks ট্র্যাক করুন। মডেলের প্রতিক্রিয়া দেরিতে এলে সেই ব্লকের জন্য কোনো অর্ডার পোস্ট করা হয়নি।
  • ইনটেন্ট ও কনফার্মেশন আলাদা রাখুন: status: "sent"-কে প্লেসড অর্ডার হিসেবে গণ্য করবেন না। পরবর্তী quote ইভেন্টে placed, reverted বা lost না আসা পর্যন্ত অপেক্ষা করুন।
  • ক্যাপযুক্ত কোট ট্র্যাক করুন: capped সত্য হলে, জমা দেওয়া সাইডটি অপরিবর্তিত মডেল সম্ভাবনার সঙ্গে তুলনা করুন, যাতে এক্সিকিউশন সিগন্যাল থেকে কেন বিচ্যুত হয়েছে তা বোঝা যায়।
  • SSE স্ন্যাপশট ব্যবহার করুন: নতুন মনিটরিং ক্লায়েন্টগুলোকে প্রথম snapshot ইভেন্ট থেকে শুরু করতে হবে, এরপর ব্লক, কোট ও ফিলের পরবর্তী আপডেট প্রয়োগ করতে হবে।
  • উভয় RPC পথের পরিমাপ করুন: ধীর রিড সিদ্ধান্তে বিলম্ব ঘটায়, আর ধীর সাবমিশনের কারণে মার্কেট পরিবর্তনের আগে কোনো কোট পৌঁছানোর সম্ভাবনা কমে যায়।
  • চার্জ হওয়া গ্যাস লিমিট বিবেচনায় রাখুন: রিভার্ট হওয়া ট্রানজ্যাকশনেও উল্লেখযোগ্য খরচ হতে পারে। তাই শুধু ফিল মূল্যায়ন না করে gasMon, gasUsd এবং রিভার্শনের মোট সংখ্যা পর্যবেক্ষণ করুন।
  • ড্রাই-রান প্রিন্ট দিয়ে যাচাই করুন: সিমুলেটেড ফিল একটি অর্ডার-দাম অতিক্রম করা বাস্তব প্রিন্টের ওপর নির্ভর করে। ফলে তা তাৎক্ষণিক ফিল ধরে নেওয়ার চেয়ে বেশি বাস্তবসম্মত, যদিও প্রকৃত কিউ পজিশন ও এক্সিকিউশন থেকে ভিন্ন।

উপসংহার

jev-trader একটি লাইভ Kuru বুক রিডার, একটি মক বা Jev সিদ্ধান্ত মডেল, অপ্টিমাইজ করা Monad ট্রানজ্যাকশন সাবমিশন, পজিশন অ্যাকাউন্টিং এবং একটি SSE মনিটরিং সার্ভিস একত্র করে। PRIVATE_KEY ছাড়া শুরু করুন, স্ন্যাপশট ও ইভেন্ট স্ট্রিম পরীক্ষা করুন, ক্রিটিক্যাল পাথ বেঞ্চমার্ক করুন এবং লাইভ এক্সিকিউশন বিবেচনা করার আগে এনকোডিং যাচাই করুন। প্রোডাকশনে কনফার্মেশন, দেরিতে আসা ব্লক, ক্যাপ, রিভার্সন, ইনভেন্টরি, P&L এবং গ্যাস খরচ একসঙ্গে পর্যবেক্ষণ করুন।