update
This commit is contained in:
parent
ac32800ba7
commit
7cc42ee6a1
7 changed files with 82 additions and 82 deletions
|
|
@ -20,7 +20,7 @@ def get_user_api(user_id: str):
|
||||||
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
|
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
|
||||||
|
|
||||||
@router.get("/account", response_model=AccountResponseSchema, tags=["Account"])
|
@router.get("/account", response_model=AccountResponseSchema, tags=["Account"])
|
||||||
def get_account_info(userId: str = Query(..., description="ID của user để lấy API key/secret")):
|
def get_account_info(userId: str = Query(..., description="User ID to get API key/secret")):
|
||||||
try:
|
try:
|
||||||
api_key, api_secret = get_user_api(userId)
|
api_key, api_secret = get_user_api(userId)
|
||||||
client = get_bybit_client(api_key, api_secret)
|
client = get_bybit_client(api_key, api_secret)
|
||||||
|
|
@ -31,4 +31,4 @@ def get_account_info(userId: str = Query(..., description="ID của user để l
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}")
|
raise HTTPException(status_code=500, detail=f"System error: {e}")
|
||||||
|
|
|
||||||
|
|
@ -45,17 +45,17 @@ def rsi(series, period=14):
|
||||||
|
|
||||||
@router.get("/candles", response_model=CandleResponseSchema, response_model_exclude_none=True, tags=["Candle"])
|
@router.get("/candles", response_model=CandleResponseSchema, response_model_exclude_none=True, tags=["Candle"])
|
||||||
def get_candles(
|
def get_candles(
|
||||||
userId: str = Query(..., description="ID của user để lấy API key/secret"),
|
userId: str = Query(..., description="User ID to get API key/secret"),
|
||||||
symbol: str = Query(..., description="Mã giao dịch, ví dụ: BTCUSDT"),
|
symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"),
|
||||||
interval: str = Query("1", description="Khung thời gian nến, ví dụ: 1, 3, 5, 15, 30, 60, 240, D, W, M"),
|
interval: str = Query("1", description="Candle interval, e.g. 1, 3, 5, 15, 30, 60, 240, D, W, M"),
|
||||||
limit: Optional[int] = Query(200, description="Số lượng nến trả về (tối đa 1000)"),
|
limit: Optional[int] = Query(200, description="Number of candles to return (max 1000)"),
|
||||||
start: Optional[int] = Query(None, description="Timestamp bắt đầu (miliseconds)"),
|
start: Optional[int] = Query(None, description="Start timestamp (milliseconds)"),
|
||||||
end: Optional[int] = Query(None, description="Timestamp kết thúc (miliseconds)")
|
end: Optional[int] = Query(None, description="End timestamp (milliseconds)")
|
||||||
):
|
):
|
||||||
api_key, api_secret = get_user_api(userId)
|
api_key, api_secret = get_user_api(userId)
|
||||||
client = get_bybit_client(api_key, api_secret)
|
client = get_bybit_client(api_key, api_secret)
|
||||||
params = {
|
params = {
|
||||||
"category": "linear", # hoặc "spot" nếu lấy spot
|
"category": "linear", # or "spot" for spot
|
||||||
"symbol": symbol,
|
"symbol": symbol,
|
||||||
"interval": interval,
|
"interval": interval,
|
||||||
"limit": limit
|
"limit": limit
|
||||||
|
|
@ -70,7 +70,7 @@ def get_candles(
|
||||||
candles = res.get('result', {}).get('list', [])
|
candles = res.get('result', {}).get('list', [])
|
||||||
if not candles:
|
if not candles:
|
||||||
return {"data": [], "indicators": {}}
|
return {"data": [], "indicators": {}}
|
||||||
# Chuyển đổi sang DataFrame
|
# Convert to DataFrame
|
||||||
df = pd.DataFrame(candles, columns=[
|
df = pd.DataFrame(candles, columns=[
|
||||||
"timestamp", "open", "high", "low", "close", "volume", "turnover"
|
"timestamp", "open", "high", "low", "close", "volume", "turnover"
|
||||||
])
|
])
|
||||||
|
|
@ -84,7 +84,7 @@ def get_candles(
|
||||||
"turnover": np.float64
|
"turnover": np.float64
|
||||||
})
|
})
|
||||||
df = df.sort_values("timestamp", ascending=False)
|
df = df.sort_values("timestamp", ascending=False)
|
||||||
# Tính chỉ báo
|
# Calculate indicators
|
||||||
close = df["close"]
|
close = df["close"]
|
||||||
macd_line, macd_signal, macd_hist = macd(close, fast=45, slow=90, signal=9)
|
macd_line, macd_signal, macd_hist = macd(close, fast=45, slow=90, signal=9)
|
||||||
df["macd"] = macd_line
|
df["macd"] = macd_line
|
||||||
|
|
@ -95,7 +95,7 @@ def get_candles(
|
||||||
df["ema100"] = ema(close, 100)
|
df["ema100"] = ema(close, 100)
|
||||||
df["ema200"] = ema(close, 200)
|
df["ema200"] = ema(close, 200)
|
||||||
df["rsi"] = rsi(close)
|
df["rsi"] = rsi(close)
|
||||||
# Trả về kết quả
|
# Return result
|
||||||
data = df.replace({np.nan: None}).to_dict(orient="records")
|
data = df.replace({np.nan: None}).to_dict(orient="records")
|
||||||
return {
|
return {
|
||||||
"data": data,
|
"data": data,
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ def get_user_api(user_id: str):
|
||||||
|
|
||||||
@router.get("/orders", tags=["Order"])
|
@router.get("/orders", tags=["Order"])
|
||||||
def get_orders(
|
def get_orders(
|
||||||
userId: str = Query(..., description="ID của user để lấy API key/secret"),
|
userId: str = Query(..., description="User ID to get API key/secret"),
|
||||||
symbol: str = Query(..., description="Mã giao dịch, ví dụ: BTCUSDT"),
|
symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"),
|
||||||
category: str = Query("linear", description="Loại lệnh: linear (future) hoặc spot")
|
category: str = Query("linear", description="Order type: linear (future) or spot")
|
||||||
):
|
):
|
||||||
api_key, api_secret = get_user_api(userId)
|
api_key, api_secret = get_user_api(userId)
|
||||||
client = get_bybit_client(api_key, api_secret)
|
client = get_bybit_client(api_key, api_secret)
|
||||||
res = client.get_open_orders(
|
res = client.get_open_orders(
|
||||||
category=category, # "linear" cho future, "spot" cho spot
|
category=category, # "linear" for future, "spot" for spot
|
||||||
symbol=symbol
|
symbol=symbol
|
||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
|
@ -36,13 +36,13 @@ def get_orders(
|
||||||
# Submit order
|
# Submit order
|
||||||
@router.post("/orders", tags=["Order"])
|
@router.post("/orders", tags=["Order"])
|
||||||
def submit_order(
|
def submit_order(
|
||||||
userId: str = Body(..., embed=True, description="ID của user để lấy API key/secret"),
|
userId: str = Body(..., embed=True, description="User ID to get API key/secret"),
|
||||||
symbol: str = Body(..., embed=True, description="Mã giao dịch, ví dụ: BTCUSDT"),
|
symbol: str = Body(..., embed=True, description="Trading symbol, e.g. BTCUSDT"),
|
||||||
side: str = Body(..., embed=True, description="side: Buy hoặc Sell"),
|
side: str = Body(..., embed=True, description="Order side: Buy or Sell"),
|
||||||
orderType: str = Body(..., embed=True, description="Loại lệnh: Market hoặc Limit"),
|
orderType: str = Body(..., embed=True, description="Order type: Market or Limit"),
|
||||||
qty: float = Body(..., embed=True, description="Số lượng"),
|
qty: float = Body(..., embed=True, description="Order quantity"),
|
||||||
category: str = Body("linear", embed=True, description="Loại lệnh: linear (future) hoặc spot"),
|
category: str = Body("linear", embed=True, description="Order type: linear (future) or spot"),
|
||||||
price: Optional[float] = Body(None, embed=True, description="Giá (bắt buộc với Limit)")
|
price: Optional[float] = Body(None, embed=True, description="Order price (required for Limit)")
|
||||||
):
|
):
|
||||||
api_key, api_secret = get_user_api(userId)
|
api_key, api_secret = get_user_api(userId)
|
||||||
client = get_bybit_client(api_key, api_secret)
|
client = get_bybit_client(api_key, api_secret)
|
||||||
|
|
@ -55,7 +55,7 @@ def submit_order(
|
||||||
}
|
}
|
||||||
if orderType.lower() == "limit":
|
if orderType.lower() == "limit":
|
||||||
if price is None:
|
if price is None:
|
||||||
raise HTTPException(status_code=400, detail="Thiếu giá cho lệnh Limit")
|
raise HTTPException(status_code=400, detail="Missing price for Limit order")
|
||||||
params["price"] = price
|
params["price"] = price
|
||||||
res = client.place_order(**params)
|
res = client.place_order(**params)
|
||||||
return res
|
return res
|
||||||
|
|
@ -64,9 +64,9 @@ def submit_order(
|
||||||
@router.delete("/orders/{order_id}", tags=["Order"])
|
@router.delete("/orders/{order_id}", tags=["Order"])
|
||||||
def cancel_order(
|
def cancel_order(
|
||||||
order_id: str,
|
order_id: str,
|
||||||
userId: str = Query(..., description="ID của user để lấy API key/secret"),
|
userId: str = Query(..., description="User ID to get API key/secret"),
|
||||||
symbol: str = Query(..., description="Mã giao dịch, ví dụ: BTCUSDT"),
|
symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"),
|
||||||
category: str = Query("linear", description="Loại lệnh: linear (future) hoặc spot")
|
category: str = Query("linear", description="Order type: linear (future) or spot")
|
||||||
):
|
):
|
||||||
api_key, api_secret = get_user_api(userId)
|
api_key, api_secret = get_user_api(userId)
|
||||||
client = get_bybit_client(api_key, api_secret)
|
client = get_bybit_client(api_key, api_secret)
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ def get_user_api(user_id: str):
|
||||||
|
|
||||||
@router.get("/positions", tags=["Position"])
|
@router.get("/positions", tags=["Position"])
|
||||||
def get_positions(
|
def get_positions(
|
||||||
userId: str = Query(..., description="ID của user để lấy API key/secret"),
|
userId: str = Query(..., description="User ID to get API key/secret"),
|
||||||
symbol: Optional[str] = Query(None, description="Mã giao dịch, ví dụ: BTCUSDT"),
|
symbol: Optional[str] = Query(None, description="Trading symbol, e.g. BTCUSDT"),
|
||||||
category: str = Query("linear", description="Loại lệnh: linear (future) hoặc spot")
|
category: str = Query("linear", description="Order type: linear (future) or spot")
|
||||||
):
|
):
|
||||||
api_key, api_secret = get_user_api(userId)
|
api_key, api_secret = get_user_api(userId)
|
||||||
client = get_bybit_client(api_key, api_secret)
|
client = get_bybit_client(api_key, api_secret)
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,21 @@ def list_users():
|
||||||
accounts = json.load(f)
|
accounts = json.load(f)
|
||||||
return accounts
|
return accounts
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}")
|
raise HTTPException(status_code=500, detail=f"Error reading accounts file: {e}")
|
||||||
|
|
||||||
# API thêm user
|
# API thêm user
|
||||||
@router.post("/users", tags=["User"])
|
@router.post("/users", tags=["User"])
|
||||||
def add_user(
|
def add_user(
|
||||||
id: str = Body(..., description="ID user"),
|
id: str = Body(..., description="User ID"),
|
||||||
bybit_api_key: str = Body(..., description="Bybit API Key"),
|
bybit_api_key: str = Body(..., description="Bybit API Key"),
|
||||||
bybit_api_secret: str = Body(..., description="Bybit API Secret"),
|
bybit_api_secret: str = Body(..., description="Bybit API Secret"),
|
||||||
user_name: str = Body(..., description="Tên user")
|
user_name: str = Body(..., description="User name")
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f:
|
with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f:
|
||||||
accounts = json.load(f)
|
accounts = json.load(f)
|
||||||
if any(acc['id'] == id for acc in accounts):
|
if any(acc['id'] == id for acc in accounts):
|
||||||
raise HTTPException(status_code=400, detail="ID đã tồn tại")
|
raise HTTPException(status_code=400, detail="ID already exists")
|
||||||
new_user = {
|
new_user = {
|
||||||
"id": id,
|
"id": id,
|
||||||
"bybit_api_key": bybit_api_key,
|
"bybit_api_key": bybit_api_key,
|
||||||
|
|
@ -40,11 +40,11 @@ def add_user(
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
json.dump(accounts, f, ensure_ascii=False, indent=2)
|
json.dump(accounts, f, ensure_ascii=False, indent=2)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
return {"message": "Thêm user thành công"}
|
return {"message": "User added successfully"}
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
|
raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}")
|
||||||
|
|
||||||
# API sửa user
|
# API sửa user
|
||||||
@router.put("/users/{user_id}", tags=["User"])
|
@router.put("/users/{user_id}", tags=["User"])
|
||||||
|
|
@ -68,12 +68,12 @@ def update_user(
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
json.dump(accounts, f, ensure_ascii=False, indent=2)
|
json.dump(accounts, f, ensure_ascii=False, indent=2)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
return {"message": "Cập nhật user thành công"}
|
return {"message": "User updated successfully"}
|
||||||
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
|
raise HTTPException(status_code=404, detail="User ID not found")
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
|
raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}")
|
||||||
|
|
||||||
# API xóa user
|
# API xóa user
|
||||||
@router.delete("/users/{user_id}", tags=["User"])
|
@router.delete("/users/{user_id}", tags=["User"])
|
||||||
|
|
@ -83,12 +83,12 @@ def delete_user(user_id: str):
|
||||||
accounts = json.load(f)
|
accounts = json.load(f)
|
||||||
new_accounts = [acc for acc in accounts if acc['id'] != user_id]
|
new_accounts = [acc for acc in accounts if acc['id'] != user_id]
|
||||||
if len(new_accounts) == len(accounts):
|
if len(new_accounts) == len(accounts):
|
||||||
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
|
raise HTTPException(status_code=404, detail="User ID not found")
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
json.dump(new_accounts, f, ensure_ascii=False, indent=2)
|
json.dump(new_accounts, f, ensure_ascii=False, indent=2)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
return {"message": "Xóa user thành công"}
|
return {"message": "User deleted successfully"}
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
|
raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}")
|
||||||
|
|
@ -2,45 +2,45 @@ from pydantic import BaseModel, Field
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any
|
||||||
|
|
||||||
class CoinInfo(BaseModel):
|
class CoinInfo(BaseModel):
|
||||||
availableToBorrow: Optional[str] = Field(None, description="Số lượng có thể vay")
|
availableToBorrow: Optional[str] = Field(None, description="Amount available to borrow")
|
||||||
bonus: Optional[str] = Field(None, description="Tiền thưởng")
|
bonus: Optional[str] = Field(None, description="Bonus amount")
|
||||||
accruedInterest: Optional[str] = Field(None, description="Lãi tích lũy")
|
accruedInterest: Optional[str] = Field(None, description="Accrued interest")
|
||||||
availableToWithdraw: Optional[str] = Field(None, description="Số lượng có thể rút")
|
availableToWithdraw: Optional[str] = Field(None, description="Amount available to withdraw")
|
||||||
totalOrderIM: Optional[str] = Field(None, description="Initial margin cho lệnh")
|
totalOrderIM: Optional[str] = Field(None, description="Initial margin for orders")
|
||||||
equity: Optional[str] = Field(None, description="Tổng tài sản coin")
|
equity: Optional[str] = Field(None, description="Total coin equity")
|
||||||
totalPositionMM: Optional[str] = Field(None, description="Maintenance margin cho vị thế")
|
totalPositionMM: Optional[str] = Field(None, description="Maintenance margin for positions")
|
||||||
usdValue: Optional[str] = Field(None, description="Giá trị USD của coin")
|
usdValue: Optional[str] = Field(None, description="USD value of the coin")
|
||||||
unrealisedPnl: Optional[str] = Field(None, description="Lãi/lỗ chưa chốt")
|
unrealisedPnl: Optional[str] = Field(None, description="Unrealized PnL")
|
||||||
collateralSwitch: Optional[bool] = Field(None, description="Có dùng làm tài sản thế chấp không")
|
collateralSwitch: Optional[bool] = Field(None, description="Is used as collateral")
|
||||||
spotHedgingQty: Optional[str] = Field(None, description="Số lượng spot hedging")
|
spotHedgingQty: Optional[str] = Field(None, description="Spot hedging quantity")
|
||||||
borrowAmount: Optional[str] = Field(None, description="Số lượng đã vay")
|
borrowAmount: Optional[str] = Field(None, description="Borrowed amount")
|
||||||
totalPositionIM: Optional[str] = Field(None, description="Initial margin cho vị thế")
|
totalPositionIM: Optional[str] = Field(None, description="Initial margin for positions")
|
||||||
walletBalance: Optional[str] = Field(None, description="Số dư ví coin")
|
walletBalance: Optional[str] = Field(None, description="Coin wallet balance")
|
||||||
cumRealisedPnl: Optional[str] = Field(None, description="Lãi/lỗ đã chốt tích lũy")
|
cumRealisedPnl: Optional[str] = Field(None, description="Cumulative realized PnL")
|
||||||
locked: Optional[str] = Field(None, description="Số lượng bị khóa")
|
locked: Optional[str] = Field(None, description="Locked amount")
|
||||||
marginCollateral: Optional[bool] = Field(None, description="Có dùng làm tài sản margin không")
|
marginCollateral: Optional[bool] = Field(None, description="Is used as margin collateral")
|
||||||
coin: str = Field(..., description="Mã coin")
|
coin: str = Field(..., description="Coin symbol")
|
||||||
|
|
||||||
class AccountInfo(BaseModel):
|
class AccountInfo(BaseModel):
|
||||||
totalEquity: str = Field(..., description="Tổng tài sản quy USD")
|
totalEquity: str = Field(..., description="Total equity in USD")
|
||||||
accountIMRate: str = Field(..., description="Initial margin rate")
|
accountIMRate: str = Field(..., description="Initial margin rate")
|
||||||
totalMarginBalance: str = Field(..., description="Tổng margin balance")
|
totalMarginBalance: str = Field(..., description="Total margin balance")
|
||||||
totalInitialMargin: str = Field(..., description="Tổng initial margin")
|
totalInitialMargin: str = Field(..., description="Total initial margin")
|
||||||
accountType: str = Field(..., description="Loại tài khoản (ví dụ: UNIFIED)")
|
accountType: str = Field(..., description="Account type (e.g. UNIFIED)")
|
||||||
totalAvailableBalance: str = Field(..., description="Tổng số dư khả dụng")
|
totalAvailableBalance: str = Field(..., description="Total available balance")
|
||||||
accountMMRate: str = Field(..., description="Maintenance margin rate")
|
accountMMRate: str = Field(..., description="Maintenance margin rate")
|
||||||
totalPerpUPL: str = Field(..., description="Lãi/lỗ chưa chốt của perpetual")
|
totalPerpUPL: str = Field(..., description="Unrealized PnL of perpetual positions")
|
||||||
totalWalletBalance: str = Field(..., description="Tổng số dư ví")
|
totalWalletBalance: str = Field(..., description="Total wallet balance")
|
||||||
accountLTV: str = Field(..., description="Loan to value")
|
accountLTV: str = Field(..., description="Loan to value")
|
||||||
totalMaintenanceMargin: str = Field(..., description="Tổng maintenance margin")
|
totalMaintenanceMargin: str = Field(..., description="Total maintenance margin")
|
||||||
coin: List[CoinInfo] = Field(..., description="Danh sách coin")
|
coin: List[CoinInfo] = Field(..., description="List of coins")
|
||||||
|
|
||||||
class ResultInfo(BaseModel):
|
class ResultInfo(BaseModel):
|
||||||
list: List[AccountInfo] = Field(..., description="Danh sách tài khoản")
|
list: List[AccountInfo] = Field(..., description="List of accounts")
|
||||||
|
|
||||||
class AccountResponseSchema(BaseModel):
|
class AccountResponseSchema(BaseModel):
|
||||||
retCode: int = Field(..., description="Mã kết quả trả về (0 là thành công)")
|
retCode: int = Field(..., description="Return code (0 means success)")
|
||||||
retMsg: str = Field(..., description="Thông báo kết quả")
|
retMsg: str = Field(..., description="Return message")
|
||||||
result: ResultInfo = Field(..., description="Kết quả chi tiết")
|
result: ResultInfo = Field(..., description="Detailed result")
|
||||||
retExtInfo: Dict[str, Any] = Field(..., description="Thông tin mở rộng")
|
retExtInfo: Dict[str, Any] = Field(..., description="Extra information")
|
||||||
time: int = Field(..., description="Timestamp trả về")
|
time: int = Field(..., description="Response timestamp")
|
||||||
|
|
@ -2,13 +2,13 @@ from pydantic import BaseModel, Field
|
||||||
from typing import List, Optional, Any
|
from typing import List, Optional, Any
|
||||||
|
|
||||||
class CandleSchema(BaseModel):
|
class CandleSchema(BaseModel):
|
||||||
timestamp: int = Field(..., description="Thời gian (miliseconds)")
|
timestamp: int = Field(..., description="Timestamp (milliseconds)")
|
||||||
open: float = Field(..., description="Giá mở cửa")
|
open: float = Field(..., description="Open price")
|
||||||
high: float = Field(..., description="Giá cao nhất")
|
high: float = Field(..., description="High price")
|
||||||
low: float = Field(..., description="Giá thấp nhất")
|
low: float = Field(..., description="Low price")
|
||||||
close: float = Field(..., description="Giá đóng cửa")
|
close: float = Field(..., description="Close price")
|
||||||
volume: float = Field(..., description="Khối lượng giao dịch")
|
volume: float = Field(..., description="Volume")
|
||||||
turnover: float = Field(..., description="Giá trị giao dịch")
|
turnover: float = Field(..., description="Turnover value")
|
||||||
macd: Optional[float] = Field(None, description="MACD value")
|
macd: Optional[float] = Field(None, description="MACD value")
|
||||||
macdsignal: Optional[float] = Field(None, description="MACD signal")
|
macdsignal: Optional[float] = Field(None, description="MACD signal")
|
||||||
macdhist: Optional[float] = Field(None, description="MACD histogram")
|
macdhist: Optional[float] = Field(None, description="MACD histogram")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue