bybit functionality
This commit is contained in:
parent
68c2848052
commit
2fb62bd04a
|
|
@ -5,3 +5,6 @@ TELEGRAM_API_HASH=telegram_api_hash_placeholder
|
|||
TELEGRAM_SESSION_NAME=telegram_session_name_placeholder
|
||||
BINANCE_API_KEY=binance_api_key_placeholder
|
||||
TAAPI_API_KEY=taapi_api_key_placeholder
|
||||
BYBIT_BASE_URL=https://api-demo.bybit.com
|
||||
BYBIT_API_KEY=bybit_api_key_placeholder
|
||||
BYBIT_API_SECRET=bybit_api_secret_placeholder
|
||||
|
|
|
|||
|
|
@ -10,5 +10,4 @@ eval_data/
|
|||
*.egg-info/
|
||||
.env
|
||||
.python-version
|
||||
playground.ipynb
|
||||
*session
|
||||
|
|
|
|||
|
|
@ -0,0 +1,290 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ab4c0304",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.binance import get_market_data\n",
|
||||
"\n",
|
||||
"print(get_market_data('BTC/USDT', '2025-12-20', '2025-12-25'))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a58bb868",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.taapi import get_crypto_stats_indicators_window\n",
|
||||
"print(get_crypto_stats_indicators_window('BTC/USDT', 'macd', '2025-12-20', 3))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "60a3bc38",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.taapi import get_crypto_stats_indicators\n",
|
||||
"print(get_crypto_stats_indicators('BTC/USDT', ['rsi', 'macd'], '2025-12-25', 30))\n",
|
||||
" # 08:42:55 [Tool Call] get_indicators_bulk(symbol=BTCUSDT, indicators=['sma', 'rsi', 'macd', 'bbands', 'atr'], curr_date=2025-12-26, look_back_days=30))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3f341582",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.openai import get_whitepaper_openai\n",
|
||||
"whitepaper = get_whitepaper_openai('BTC/USDT')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6db71e93",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(whitepaper.output[0].content[0].text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c00b90b8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.openai import get_fundamentals_openai\n",
|
||||
"fundamentals = get_fundamentals_openai('BTC/USDT', '2025-12-25')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "01b03899",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(fundamentals)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "15fd34ce",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Clean test of market_analyst only\n",
|
||||
"print(\"=== CLEAN MARKET ANALYST TEST ===\")\n",
|
||||
"print()\n",
|
||||
"\n",
|
||||
"# Import required modules\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from tradingagents.agents.analysts.market_analyst import create_market_analyst\n",
|
||||
"from datetime import date\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# Create LLM client\n",
|
||||
"llm = ChatOpenAI(\n",
|
||||
" model=\"gpt-4o-mini\", \n",
|
||||
" temperature=0.1,\n",
|
||||
" api_key=os.getenv(\"OPENAI_API_KEY\")\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create market analyst\n",
|
||||
"market_analyst = create_market_analyst(llm)\n",
|
||||
"\n",
|
||||
"# Correct test state with proper message format\n",
|
||||
"test_state = {\n",
|
||||
" \"trade_date\": \"2025-12-25\",\n",
|
||||
" \"ticker_of_interest\": \"BTC/USDT\",\n",
|
||||
" \"messages\": [HumanMessage(content=\"Analyze BTC/USDT market conditions\")]\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"print(f\"Testing market analyst for: {test_state['ticker_of_interest']}\")\n",
|
||||
"print(f\"Date: {test_state['trade_date']}\")\n",
|
||||
"print(\"\\nRunning market analysis...\")\n",
|
||||
"print(\"-\" * 40)\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" # Run the market analyst\n",
|
||||
" result = market_analyst(test_state)\n",
|
||||
" \n",
|
||||
" print(\"✅ SUCCESS! Market analysis completed.\")\n",
|
||||
" \n",
|
||||
" # Print the analysis result\n",
|
||||
" if \"messages\" in result and result[\"messages\"]:\n",
|
||||
" latest_message = result[\"messages\"][-1]\n",
|
||||
" \n",
|
||||
" # Check if it made tool calls\n",
|
||||
" if hasattr(latest_message, 'tool_calls') and latest_message.tool_calls:\n",
|
||||
" print(f\"\\n📊 Tools called: {len(latest_message.tool_calls)}\")\n",
|
||||
" for i, call in enumerate(latest_message.tool_calls):\n",
|
||||
" tool_name = call.get('name', 'unknown')\n",
|
||||
" print(f\" {i+1}. {tool_name}\")\n",
|
||||
" \n",
|
||||
" # Print the content/analysis\n",
|
||||
" if hasattr(latest_message, 'content') and latest_message.content:\n",
|
||||
" print(f\"\\n📝 Market Analysis:\")\n",
|
||||
" print(\"=\" * 50)\n",
|
||||
" print(latest_message.content)\n",
|
||||
" \n",
|
||||
" # Print market report if available\n",
|
||||
" if \"market_report\" in result and result[\"market_report\"]:\n",
|
||||
" print(f\"\\n📋 Market Report:\")\n",
|
||||
" print(\"=\" * 50)\n",
|
||||
" print(result[\"market_report\"])\n",
|
||||
" \n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"❌ ERROR: {e}\")\n",
|
||||
" import traceback\n",
|
||||
" traceback.print_exc()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "fcf9fe21",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"5000.62453368\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.bybit import get_wallet_balance\n",
|
||||
"balance = get_wallet_balance('USDT')\n",
|
||||
"print(balance)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "1dfa3165",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ValueError",
|
||||
"evalue": "Failed to place order: Bybit API error: An unknown parameter was sent.",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||||
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/halalquant/TradingAgents/tradingagents/dataflows/bybit.py:292\u001b[39m, in \u001b[36mplace_order\u001b[39m\u001b[34m(symbol, side, order_type, qty, price, stop_loss, take_profit, time_in_force, account_type, category, order_link_id, reduce_only, close_on_trigger)\u001b[39m\n\u001b[32m 291\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m292\u001b[39m data = \u001b[43mbybit_v5_request\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mPOST\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m/v5/order/create\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbody\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 293\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m data.get(\u001b[33m\"\u001b[39m\u001b[33mresult\u001b[39m\u001b[33m\"\u001b[39m, {})\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/halalquant/TradingAgents/tradingagents/dataflows/bybit.py:61\u001b[39m, in \u001b[36mbybit_v5_request\u001b[39m\u001b[34m(method, path, params, body)\u001b[39m\n\u001b[32m 60\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m data.get(\u001b[33m\"\u001b[39m\u001b[33mretCode\u001b[39m\u001b[33m\"\u001b[39m) != \u001b[32m0\u001b[39m:\n\u001b[32m---> \u001b[39m\u001b[32m61\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mBybit API error: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdata.get(\u001b[33m'\u001b[39m\u001b[33mretMsg\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 63\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m data\n",
|
||||
"\u001b[31mValueError\u001b[39m: Bybit API error: An unknown parameter was sent.",
|
||||
"\nDuring handling of the above exception, another exception occurred:\n",
|
||||
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
|
||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mtradingagents\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mdataflows\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mbybit\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m place_order, place_spot_order_with_sl_tp\n\u001b[32m 2\u001b[39m \u001b[38;5;66;03m# Place a spot limit order with stop loss and take profit\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m result = \u001b[43mplace_spot_order_with_sl_tp\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[43m \u001b[49m\u001b[43msymbol\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mBTCUSDT\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 5\u001b[39m \u001b[43m \u001b[49m\u001b[43mside\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mBuy\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 6\u001b[39m \u001b[43m \u001b[49m\u001b[43mqty\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m0.001\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 7\u001b[39m \u001b[43m \u001b[49m\u001b[43mprice\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m88000.0\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 8\u001b[39m \u001b[43m \u001b[49m\u001b[43mstop_loss_price\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m85000.0\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 9\u001b[39m \u001b[43m \u001b[49m\u001b[43mtake_profit_price\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m90000.0\u001b[39;49m\n\u001b[32m 10\u001b[39m \u001b[43m)\u001b[49m\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# # Place a market order\u001b[39;00m\n\u001b[32m 13\u001b[39m \u001b[38;5;66;03m# result = place_order(\u001b[39;00m\n\u001b[32m 14\u001b[39m \u001b[38;5;66;03m# symbol=\"BTCUSDT\",\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 18\u001b[39m \u001b[38;5;66;03m# category=\"spot\"\u001b[39;00m\n\u001b[32m 19\u001b[39m \u001b[38;5;66;03m# )\u001b[39;00m\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/halalquant/TradingAgents/tradingagents/dataflows/bybit.py:322\u001b[39m, in \u001b[36mplace_spot_order_with_sl_tp\u001b[39m\u001b[34m(symbol, side, qty, price, stop_loss_price, take_profit_price, order_type)\u001b[39m\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mplace_spot_order_with_sl_tp\u001b[39m(\n\u001b[32m 299\u001b[39m symbol: \u001b[38;5;28mstr\u001b[39m,\n\u001b[32m 300\u001b[39m side: \u001b[38;5;28mstr\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 305\u001b[39m order_type: \u001b[38;5;28mstr\u001b[39m = \u001b[33m\"\u001b[39m\u001b[33mLimit\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 306\u001b[39m ) -> Dict:\n\u001b[32m 307\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 308\u001b[39m \u001b[33;03m Convenience function to place a spot order with stop loss and take profit.\u001b[39;00m\n\u001b[32m 309\u001b[39m \u001b[33;03m \u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 320\u001b[39m \u001b[33;03m Dict containing order result\u001b[39;00m\n\u001b[32m 321\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m322\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mplace_order\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 323\u001b[39m \u001b[43m \u001b[49m\u001b[43msymbol\u001b[49m\u001b[43m=\u001b[49m\u001b[43msymbol\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 324\u001b[39m \u001b[43m \u001b[49m\u001b[43mside\u001b[49m\u001b[43m=\u001b[49m\u001b[43mside\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 325\u001b[39m \u001b[43m \u001b[49m\u001b[43morder_type\u001b[49m\u001b[43m=\u001b[49m\u001b[43morder_type\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 326\u001b[39m \u001b[43m \u001b[49m\u001b[43mqty\u001b[49m\u001b[43m=\u001b[49m\u001b[43mqty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 327\u001b[39m \u001b[43m \u001b[49m\u001b[43mprice\u001b[49m\u001b[43m=\u001b[49m\u001b[43mprice\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 328\u001b[39m \u001b[43m \u001b[49m\u001b[43mstop_loss\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstop_loss_price\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 329\u001b[39m \u001b[43m \u001b[49m\u001b[43mtake_profit\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtake_profit_price\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 330\u001b[39m \u001b[43m \u001b[49m\u001b[43mcategory\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mspot\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\n\u001b[32m 331\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/halalquant/TradingAgents/tradingagents/dataflows/bybit.py:295\u001b[39m, in \u001b[36mplace_order\u001b[39m\u001b[34m(symbol, side, order_type, qty, price, stop_loss, take_profit, time_in_force, account_type, category, order_link_id, reduce_only, close_on_trigger)\u001b[39m\n\u001b[32m 293\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m data.get(\u001b[33m\"\u001b[39m\u001b[33mresult\u001b[39m\u001b[33m\"\u001b[39m, {})\n\u001b[32m 294\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m--> \u001b[39m\u001b[32m295\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mFailed to place order: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
|
||||
"\u001b[31mValueError\u001b[39m: Failed to place order: Bybit API error: An unknown parameter was sent."
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from tradingagents.dataflows.bybit import place_order, place_spot_order_with_sl_tp\n",
|
||||
"# Place a spot limit order with stop loss and take profit\n",
|
||||
"result = place_spot_order_with_sl_tp(\n",
|
||||
" symbol=\"BTCUSDT\",\n",
|
||||
" side=\"Buy\",\n",
|
||||
" qty=0.001,\n",
|
||||
" price=88000.0,\n",
|
||||
" stop_loss_price=85000.0,\n",
|
||||
" take_profit_price=90000.0\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# # Place a market order\n",
|
||||
"# result = place_order(\n",
|
||||
"# symbol=\"BTCUSDT\",\n",
|
||||
"# side=\"Buy\",\n",
|
||||
"# order_type=\"Market\",\n",
|
||||
"# qty=0.001,\n",
|
||||
"# category=\"spot\"\n",
|
||||
"# )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "03bdc3c4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Order result: {'orderId': '2113891849709292800', 'orderLinkId': '2113891849709292801'}\n",
|
||||
"Order status: {'symbol': 'BTCUSDT', 'orderType': 'Limit', 'orderLinkId': '2113891849709292801', 'slLimitPrice': '27500', 'orderId': '2113891849709292800', 'cancelType': 'UNKNOWN', 'avgPrice': '0.0', 'stopOrderType': '', 'lastPriceOnCreated': '', 'orderStatus': 'New', 'takeProfit': '35000', 'cumExecValue': '0.0000000', 'smpType': 'None', 'triggerDirection': 0, 'blockTradeId': '', 'cumFeeDetail': {}, 'isLeverage': '0', 'rejectReason': 'EC_NoError', 'price': '28000.0', 'orderIv': '', 'createdTime': '1766731545590', 'tpTriggerBy': '', 'positionIdx': 0, 'trailingPercentage': '0', 'timeInForce': 'PostOnly', 'leavesValue': '280.0000000', 'basePrice': '89223.5', 'updatedTime': '1766731545591', 'side': 'Buy', 'smpGroup': 0, 'triggerPrice': '0.0', 'tpLimitPrice': '36000', 'trailingValue': '0', 'cumExecFee': '0', 'leavesQty': '0.01', 'slTriggerBy': '', 'closeOnTrigger': False, 'placeType': '', 'cumExecQty': '0', 'reduceOnly': False, 'activationPrice': '0', 'qty': '0.010000', 'stopLoss': '27000', 'marketUnit': '', 'smpOrderId': '', 'triggerBy': ''}\n",
|
||||
"Open orders: {'nextPageCursor': '2113891849709292800%3A1766731545590%2C2113891849709292800%3A1766731545590', 'category': 'spot', 'list': [{'symbol': 'BTCUSDT', 'orderType': 'Limit', 'orderLinkId': '2113891849709292801', 'slLimitPrice': '27500', 'orderId': '2113891849709292800', 'cancelType': 'UNKNOWN', 'avgPrice': '0.0', 'stopOrderType': '', 'lastPriceOnCreated': '', 'orderStatus': 'New', 'takeProfit': '35000', 'cumExecValue': '0.0000000', 'smpType': 'None', 'triggerDirection': 0, 'blockTradeId': '', 'cumFeeDetail': {}, 'isLeverage': '0', 'rejectReason': 'EC_NoError', 'price': '28000.0', 'orderIv': '', 'createdTime': '1766731545590', 'tpTriggerBy': '', 'positionIdx': 0, 'trailingPercentage': '0', 'timeInForce': 'PostOnly', 'leavesValue': '280.0000000', 'basePrice': '89223.5', 'updatedTime': '1766731545591', 'side': 'Buy', 'smpGroup': 0, 'triggerPrice': '0.0', 'tpLimitPrice': '36000', 'trailingValue': '0', 'cumExecFee': '0', 'leavesQty': '0.01', 'slTriggerBy': '', 'closeOnTrigger': False, 'placeType': '', 'cumExecQty': '0', 'reduceOnly': False, 'activationPrice': '0', 'qty': '0.010000', 'stopLoss': '27000', 'marketUnit': '', 'smpOrderId': '', 'triggerBy': ''}]}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Fixed version with correct Bybit parameters\n",
|
||||
"from tradingagents.dataflows.bybit import place_order, place_spot_order_with_sl_tp, get_order_status, get_open_orders\n",
|
||||
"\n",
|
||||
"# Place a spot limit order with stop loss and take profit - like the example\n",
|
||||
"# result = place_spot_order_with_sl_tp(\n",
|
||||
"# symbol=\"BTCUSDT\",\n",
|
||||
"# side=\"Buy\",\n",
|
||||
"# qty=0.01,\n",
|
||||
"# price=28000.0,\n",
|
||||
"# stop_loss_price=27000.0,\n",
|
||||
"# take_profit_price=35000.0,\n",
|
||||
"# sl_limit_price=27500.0, # Stop loss limit price\n",
|
||||
"# tp_limit_price=36000.0, # Take profit limit price\n",
|
||||
"# sl_order_type=\"Limit\", # Stop loss as limit order\n",
|
||||
"# tp_order_type=\"Limit\", # Take profit as limit order\n",
|
||||
"# time_in_force=\"PostOnly\" # Post only for maker orders\n",
|
||||
"# )\n",
|
||||
"\n",
|
||||
"print(\"Order result:\", result)\n",
|
||||
"# Check order status\n",
|
||||
"status = get_order_status(order_id=result.get(\"orderId\", \"2113891849709292800\"), category=\"spot\")\n",
|
||||
"\n",
|
||||
"print(\"Order status:\", status)\n",
|
||||
"\n",
|
||||
"# Get open orders\n",
|
||||
"open_orders = get_open_orders(symbol=\"BTCUSDT\", category=\"spot\")\n",
|
||||
"print(\"Open orders:\", open_orders)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import time
|
||||
from typing import Dict, Optional
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import requests
|
||||
from .config import get_config
|
||||
|
||||
|
||||
def bybit_v5_request(method: str, path: str, params: Optional[Dict] = None, body: Optional[Dict] = None) -> Dict:
|
||||
"""Generic signed HTTP request helper for Bybit V5 API."""
|
||||
config = get_config()["external"]
|
||||
base_url = config["BYBIT_BASE_URL"].rstrip("/")
|
||||
api_key = config["BYBIT_API_KEY"]
|
||||
api_secret = config["BYBIT_API_SECRET"]
|
||||
|
||||
if not api_key or not api_secret:
|
||||
raise ValueError("Missing BYBIT_API_KEY or BYBIT_API_SECRET")
|
||||
|
||||
timestamp = str(int(time.time() * 1000))
|
||||
recv_window = "5000"
|
||||
|
||||
# Build query string for GET or body for POST
|
||||
if method.upper() == "GET" and params:
|
||||
query_string = urlencode(sorted(params.items()))
|
||||
url = f"{base_url}{path}?{query_string}"
|
||||
payload = query_string
|
||||
else:
|
||||
url = f"{base_url}{path}"
|
||||
payload = json.dumps(body, separators=(',', ':')) if body else ""
|
||||
|
||||
# Create signature
|
||||
sign_payload = f"{timestamp}{api_key}{recv_window}{payload}"
|
||||
signature = hmac.new(
|
||||
api_secret.encode('utf-8'),
|
||||
sign_payload.encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
# Headers
|
||||
headers = {
|
||||
"X-BAPI-API-KEY": api_key,
|
||||
"X-BAPI-TIMESTAMP": timestamp,
|
||||
"X-BAPI-RECV-WINDOW": recv_window,
|
||||
"X-BAPI-SIGN": signature,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# Make request
|
||||
if method.upper() == "GET":
|
||||
response = requests.get(url, headers=headers)
|
||||
else:
|
||||
response = requests.post(url, headers=headers, data=payload)
|
||||
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if data.get("retCode") != 0:
|
||||
raise ValueError(f"Bybit API error: {data.get('retMsg')}")
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_wallet_balance(coin: str, account_type: str = "UNIFIED") -> float:
|
||||
"""Fetch wallet balance for a specific coin from Bybit."""
|
||||
data = bybit_v5_request("GET", "/v5/account/wallet-balance", {
|
||||
"accountType": account_type,
|
||||
"coin": coin.upper()
|
||||
})
|
||||
|
||||
# Parse response to get wallet balance
|
||||
result = data.get("result", {})
|
||||
accounts = result.get("list", [])
|
||||
|
||||
if not accounts:
|
||||
return 0.0
|
||||
|
||||
coins = accounts[0].get("coin", [])
|
||||
for coin_data in coins:
|
||||
if coin_data.get("coin") == coin.upper():
|
||||
return float(coin_data.get("walletBalance", 0))
|
||||
|
||||
return 0.0
|
||||
|
||||
|
||||
def get_order_status(order_id: str, category: str = "spot") -> Dict:
|
||||
"""
|
||||
Get order status by order ID.
|
||||
|
||||
Args:
|
||||
order_id: Order ID to query
|
||||
category: Trading category ("spot", "linear", "inverse")
|
||||
|
||||
Returns:
|
||||
Dict containing order information
|
||||
"""
|
||||
params = {
|
||||
"category": category.lower(),
|
||||
"orderId": order_id
|
||||
}
|
||||
|
||||
data = bybit_v5_request("GET", "/v5/order/realtime", params=params)
|
||||
result = data.get("result", {})
|
||||
orders = result.get("list", [])
|
||||
|
||||
return orders[0] if orders else {}
|
||||
|
||||
|
||||
def cancel_order(order_id: str, symbol: str, category: str = "spot") -> Dict:
|
||||
"""
|
||||
Cancel an existing order.
|
||||
|
||||
Args:
|
||||
order_id: Order ID to cancel
|
||||
symbol: Trading pair symbol
|
||||
category: Trading category ("spot", "linear", "inverse")
|
||||
|
||||
Returns:
|
||||
Dict containing cancellation result
|
||||
"""
|
||||
body = {
|
||||
"category": category.lower(),
|
||||
"symbol": symbol.upper(),
|
||||
"orderId": order_id
|
||||
}
|
||||
|
||||
data = bybit_v5_request("POST", "/v5/order/cancel", body=body)
|
||||
return data.get("result", {})
|
||||
|
||||
|
||||
def get_open_orders(symbol: Optional[str] = None, category: str = "spot") -> Dict:
|
||||
"""
|
||||
Get all open orders.
|
||||
|
||||
Args:
|
||||
symbol: Optional trading pair to filter by
|
||||
category: Trading category ("spot", "linear", "inverse")
|
||||
|
||||
Returns:
|
||||
Dict containing list of open orders
|
||||
"""
|
||||
params = {
|
||||
"category": category.lower()
|
||||
}
|
||||
|
||||
if symbol:
|
||||
params["symbol"] = symbol.upper()
|
||||
|
||||
data = bybit_v5_request("GET", "/v5/order/realtime", params=params)
|
||||
return data.get("result", {})
|
||||
|
||||
|
||||
def get_order_history(
|
||||
symbol: Optional[str] = None,
|
||||
category: str = "spot",
|
||||
limit: int = 20
|
||||
) -> Dict:
|
||||
"""
|
||||
Get order history.
|
||||
|
||||
Args:
|
||||
symbol: Optional trading pair to filter by
|
||||
category: Trading category ("spot", "linear", "inverse")
|
||||
limit: Number of records to return (max 50)
|
||||
|
||||
Returns:
|
||||
Dict containing order history
|
||||
"""
|
||||
params = {
|
||||
"category": category.lower(),
|
||||
"limit": min(limit, 50)
|
||||
}
|
||||
|
||||
if symbol:
|
||||
params["symbol"] = symbol.upper()
|
||||
|
||||
data = bybit_v5_request("GET", "/v5/order/history", params=params)
|
||||
return data.get("result", {})
|
||||
|
||||
|
||||
def get_account_info(account_type: str = "UNIFIED") -> Dict:
|
||||
"""
|
||||
Get account information.
|
||||
|
||||
Args:
|
||||
account_type: Account type ("UNIFIED")
|
||||
|
||||
Returns:
|
||||
Dict containing account information
|
||||
"""
|
||||
params = {
|
||||
"accountType": account_type
|
||||
}
|
||||
|
||||
data = bybit_v5_request("GET", "/v5/account/info", params=params)
|
||||
return data.get("result", {})
|
||||
|
||||
def place_order(
|
||||
symbol: str,
|
||||
side: str,
|
||||
order_type: str,
|
||||
qty: float,
|
||||
price: Optional[float] = None,
|
||||
stop_loss: Optional[float] = None,
|
||||
take_profit: Optional[float] = None,
|
||||
sl_limit_price: Optional[float] = None,
|
||||
tp_limit_price: Optional[float] = None,
|
||||
sl_order_type: str = "Market",
|
||||
tp_order_type: str = "Market",
|
||||
time_in_force: str = "GTC",
|
||||
account_type: str = "UNIFIED",
|
||||
category: str = "spot",
|
||||
order_link_id: Optional[str] = None,
|
||||
reduce_only: bool = False,
|
||||
close_on_trigger: bool = False
|
||||
) -> Dict:
|
||||
"""
|
||||
Place an order on Bybit with comprehensive support for spot trading.
|
||||
|
||||
Args:
|
||||
symbol: Trading pair symbol (e.g., "BTCUSDT")
|
||||
side: Order side ("Buy" or "Sell")
|
||||
order_type: Order type ("Market", "Limit")
|
||||
qty: Order quantity
|
||||
price: Order price (required for Limit orders)
|
||||
stop_loss: Stop loss trigger price
|
||||
take_profit: Take profit trigger price
|
||||
sl_limit_price: Stop loss limit price (for limit SL orders)
|
||||
tp_limit_price: Take profit limit price (for limit TP orders)
|
||||
sl_order_type: Stop loss order type ("Market" or "Limit")
|
||||
tp_order_type: Take profit order type ("Market" or "Limit")
|
||||
time_in_force: Time in force ("GTC", "IOC", "FOK", "PostOnly")
|
||||
account_type: Account type ("UNIFIED")
|
||||
category: Trading category ("spot", "linear", "inverse")
|
||||
order_link_id: Custom order ID
|
||||
reduce_only: Reduce only flag
|
||||
close_on_trigger: Close on trigger flag
|
||||
|
||||
Returns:
|
||||
Dict containing order result
|
||||
|
||||
Raises:
|
||||
ValueError: If required parameters are missing or invalid
|
||||
"""
|
||||
# Validate required parameters
|
||||
if not symbol or not side or not order_type:
|
||||
raise ValueError("symbol, side, and order_type are required")
|
||||
|
||||
if qty <= 0:
|
||||
raise ValueError("qty must be greater than 0")
|
||||
|
||||
# Validate order type and price requirement
|
||||
if order_type.upper() == "LIMIT" and price is None:
|
||||
raise ValueError("price is required for Limit orders")
|
||||
|
||||
# Validate side
|
||||
if side.upper() not in ["BUY", "SELL"]:
|
||||
raise ValueError("side must be 'Buy' or 'Sell'")
|
||||
|
||||
# Validate order type
|
||||
valid_order_types = ["MARKET", "LIMIT"]
|
||||
if order_type.upper() not in valid_order_types:
|
||||
raise ValueError(f"order_type must be one of {valid_order_types}")
|
||||
|
||||
# Build order body
|
||||
body = {
|
||||
"category": category.lower(),
|
||||
"symbol": symbol.upper(),
|
||||
"side": side.capitalize(),
|
||||
"orderType": order_type.capitalize(),
|
||||
"qty": str(qty),
|
||||
"timeInForce": time_in_force,
|
||||
}
|
||||
|
||||
# Add price for limit orders
|
||||
if price is not None:
|
||||
body["price"] = str(price)
|
||||
|
||||
# Add stop loss with proper formatting
|
||||
if stop_loss is not None:
|
||||
body["stopLoss"] = str(stop_loss)
|
||||
body["slOrderType"] = sl_order_type.capitalize()
|
||||
|
||||
# Add limit price for stop loss if specified
|
||||
if sl_order_type.upper() == "LIMIT" and sl_limit_price is not None:
|
||||
body["slLimitPrice"] = str(sl_limit_price)
|
||||
|
||||
# Add take profit with proper formatting
|
||||
if take_profit is not None:
|
||||
body["takeProfit"] = str(take_profit)
|
||||
body["tpOrderType"] = tp_order_type.capitalize()
|
||||
|
||||
# Add limit price for take profit if specified
|
||||
if tp_order_type.upper() == "LIMIT" and tp_limit_price is not None:
|
||||
body["tpLimitPrice"] = str(tp_limit_price)
|
||||
|
||||
# Add optional parameters
|
||||
if order_link_id:
|
||||
body["orderLinkId"] = order_link_id
|
||||
|
||||
if reduce_only:
|
||||
body["reduceOnly"] = True
|
||||
|
||||
if close_on_trigger:
|
||||
body["closeOnTrigger"] = True
|
||||
|
||||
try:
|
||||
data = bybit_v5_request("POST", "/v5/order/create", body=body)
|
||||
return data.get("result", {})
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to place order: {str(e)}")
|
||||
|
||||
def place_spot_order_with_sl_tp(
|
||||
symbol: str,
|
||||
side: str,
|
||||
qty: float,
|
||||
price: Optional[float] = None,
|
||||
stop_loss_price: Optional[float] = None,
|
||||
take_profit_price: Optional[float] = None,
|
||||
sl_limit_price: Optional[float] = None,
|
||||
tp_limit_price: Optional[float] = None,
|
||||
sl_order_type: str = "Market",
|
||||
tp_order_type: str = "Market",
|
||||
order_type: str = "Limit",
|
||||
time_in_force: str = "PostOnly"
|
||||
) -> Dict:
|
||||
"""
|
||||
Convenience function to place a spot order with stop loss and take profit.
|
||||
|
||||
Args:
|
||||
symbol: Trading pair symbol (e.g., "BTCUSDT")
|
||||
side: Order side ("Buy" or "Sell")
|
||||
qty: Order quantity
|
||||
price: Limit price (None for market orders)
|
||||
stop_loss_price: Stop loss trigger price
|
||||
take_profit_price: Take profit trigger price
|
||||
sl_limit_price: Stop loss limit price (for limit SL orders)
|
||||
tp_limit_price: Take profit limit price (for limit TP orders)
|
||||
sl_order_type: Stop loss order type ("Market" or "Limit")
|
||||
tp_order_type: Take profit order type ("Market" or "Limit")
|
||||
order_type: "Limit" or "Market"
|
||||
time_in_force: Time in force ("GTC", "IOC", "FOK", "PostOnly")
|
||||
|
||||
Returns:
|
||||
Dict containing order result
|
||||
"""
|
||||
return place_order(
|
||||
symbol=symbol,
|
||||
side=side,
|
||||
order_type=order_type,
|
||||
qty=qty,
|
||||
price=price,
|
||||
stop_loss=stop_loss_price,
|
||||
take_profit=take_profit_price,
|
||||
sl_limit_price=sl_limit_price,
|
||||
tp_limit_price=tp_limit_price,
|
||||
sl_order_type=sl_order_type,
|
||||
tp_order_type=tp_order_type,
|
||||
time_in_force=time_in_force,
|
||||
category="spot"
|
||||
)
|
||||
|
|
@ -33,6 +33,11 @@ DEFAULT_CONFIG = {
|
|||
},
|
||||
# Tool provider settings
|
||||
"tool_providers": {
|
||||
"TAAPI_BASE_URL": "https://api.taapi.io",
|
||||
"TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"),
|
||||
},
|
||||
"external": {
|
||||
"BYBIT_BASE_URL": os.getenv("BYBIT_BASE_URL", "https://api-demo.bybit.com"),
|
||||
"BYBIT_API_KEY": os.getenv("BYBIT_API_KEY", ""),
|
||||
"BYBIT_API_SECRET": os.getenv("BYBIT_API_SECRET", ""),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue