use crate::cli::common::write_json_output;
use clap::{Args, Subcommand};
use polymarket_hft::client::polymarket::data::Client;
#[derive(Subcommand)]
pub enum DataCommands {
GetUserPositions {
#[command(flatten)]
params: GetUserPositionsArgs,
},
GetUserClosedPositions {
#[command(flatten)]
params: GetUserClosedPositionsArgs,
},
GetUserPortfolioValue {
#[arg(short, long, required = true)]
user: String,
#[arg(short, long)]
market: Option<Vec<String>>,
},
GetUserTradedMarkets {
#[arg(short, long, required = true)]
user: String,
},
GetUserActivity {
#[command(flatten)]
params: GetUserActivityArgs,
},
GetTrades {
#[command(flatten)]
params: GetTradesArgs,
},
Health,
GetMarketTopHolders {
#[arg(short, long, required = true)]
market: Vec<String>,
#[arg(short, long)]
limit: Option<i32>,
#[arg(long)]
min_balance: Option<i32>,
},
GetOpenInterest {
#[arg(short, long, required = true)]
market: Vec<String>,
},
GetEventLiveVolume {
#[arg(short, long, required = true)]
id: i64,
},
}
#[derive(Args, Debug, Clone)]
pub struct GetUserPositionsArgs {
#[arg(short, long, required = true)]
pub user: String,
#[arg(short, long)]
pub market: Option<Vec<String>>,
#[arg(short, long)]
pub event_id: Option<Vec<i64>>,
#[arg(long)]
pub size_threshold: Option<f64>,
#[arg(long)]
pub redeemable: Option<bool>,
#[arg(long)]
pub mergeable: Option<bool>,
#[arg(short, long)]
pub limit: Option<i32>,
#[arg(short, long)]
pub offset: Option<i32>,
#[arg(long)]
pub sort_by: Option<String>,
#[arg(long)]
pub sort_direction: Option<String>,
#[arg(short, long)]
pub title: Option<String>,
}
#[derive(Args, Debug, Clone)]
pub struct GetUserClosedPositionsArgs {
#[arg(short, long, required = true)]
pub user: String,
#[arg(short, long)]
pub market: Option<Vec<String>>,
#[arg(short, long)]
pub title: Option<String>,
#[arg(short, long)]
pub event_id: Option<Vec<i64>>,
#[arg(short, long)]
pub limit: Option<i32>,
#[arg(short, long)]
pub offset: Option<i32>,
#[arg(long)]
pub sort_by: Option<String>,
#[arg(long)]
pub sort_direction: Option<String>,
}
#[derive(Args, Debug, Clone)]
pub struct GetUserActivityArgs {
#[arg(short, long, required = true)]
pub user: String,
#[arg(short, long)]
pub limit: Option<i32>,
#[arg(short, long)]
pub offset: Option<i32>,
#[arg(short, long)]
pub market: Option<Vec<String>>,
#[arg(short, long)]
pub event_id: Option<Vec<i64>>,
#[arg(short = 't', long = "type")]
pub activity_type: Option<Vec<String>>,
#[arg(long)]
pub start: Option<i64>,
#[arg(long)]
pub end: Option<i64>,
#[arg(long)]
pub sort_by: Option<String>,
#[arg(long)]
pub sort_direction: Option<String>,
#[arg(long)]
pub side: Option<String>,
}
#[derive(Args, Debug, Clone)]
pub struct GetTradesArgs {
#[arg(short, long)]
pub user: Option<String>,
#[arg(short, long)]
pub market: Option<Vec<String>>,
#[arg(short, long)]
pub event_id: Option<Vec<i64>>,
#[arg(short, long)]
pub limit: Option<i32>,
#[arg(short, long)]
pub offset: Option<i32>,
#[arg(long)]
pub taker_only: Option<bool>,
#[arg(long)]
pub filter_type: Option<String>,
#[arg(long)]
pub filter_amount: Option<f64>,
#[arg(short, long)]
pub side: Option<String>,
}
pub async fn handle(command: &DataCommands) -> anyhow::Result<()> {
let client = Client::new();
match command {
DataCommands::GetUserPositions { params } => {
handle_get_user_positions(&client, params).await?;
}
DataCommands::GetUserClosedPositions { params } => {
handle_get_user_closed_positions(&client, params).await?;
}
DataCommands::GetUserPortfolioValue { user, market } => {
let market_refs: Option<Vec<&str>> = market
.as_ref()
.map(|m| m.iter().map(|s| s.as_str()).collect());
let values = client
.get_user_portfolio_value(user, market_refs.as_deref())
.await?;
write_json_output(&values)?;
}
DataCommands::GetUserTradedMarkets { user } => {
let traded = client.get_user_traded_markets(user).await?;
write_json_output(&traded)?;
}
DataCommands::GetUserActivity { params } => {
handle_get_user_activity(&client, params).await?;
}
DataCommands::GetTrades { params } => {
handle_get_trades(&client, params).await?;
}
DataCommands::Health => {
let health = client.health().await?;
write_json_output(&health)?;
}
DataCommands::GetMarketTopHolders {
market,
limit,
min_balance,
} => {
let market_refs: Vec<&str> = market.iter().map(|s| s.as_str()).collect();
let holders = client
.get_market_top_holders(&market_refs, *limit, *min_balance)
.await?;
write_json_output(&holders)?;
}
DataCommands::GetOpenInterest { market } => {
let market_refs: Vec<&str> = market.iter().map(|s| s.as_str()).collect();
let oi_list = client.get_open_interest(&market_refs).await?;
write_json_output(&oi_list)?;
}
DataCommands::GetEventLiveVolume { id } => {
let volume = client.get_event_live_volume(*id).await?;
write_json_output(&volume)?;
}
}
Ok(())
}
async fn handle_get_user_positions(
client: &Client,
params: &GetUserPositionsArgs,
) -> anyhow::Result<()> {
let market_refs: Option<Vec<&str>> = params
.market
.as_ref()
.map(|m| m.iter().map(|s| s.as_str()).collect());
let parsed_sort_by = params
.sort_by
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::PositionSortBy>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --sort-by: {}", e))?;
let parsed_sort_direction = params
.sort_direction
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::SortDirection>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --sort-direction: {}", e))?;
let positions = client
.get_user_positions(
polymarket_hft::client::polymarket::data::GetUserPositionsRequest {
user: params.user.as_str(),
markets: market_refs.as_deref(),
event_ids: params.event_id.as_deref(),
size_threshold: params.size_threshold,
redeemable: params.redeemable,
mergeable: params.mergeable,
limit: params.limit,
offset: params.offset,
sort_by: parsed_sort_by,
sort_direction: parsed_sort_direction,
title: params.title.as_deref(),
},
)
.await?;
write_json_output(&positions)?;
Ok(())
}
async fn handle_get_user_closed_positions(
client: &Client,
params: &GetUserClosedPositionsArgs,
) -> anyhow::Result<()> {
let market_refs: Option<Vec<&str>> = params
.market
.as_ref()
.map(|m| m.iter().map(|s| s.as_str()).collect());
let parsed_sort_by = params
.sort_by
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::ClosedPositionSortBy>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --sort-by: {}", e))?;
let parsed_sort_direction = params
.sort_direction
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::SortDirection>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --sort-direction: {}", e))?;
let positions = client
.get_user_closed_positions(
polymarket_hft::client::polymarket::data::GetUserClosedPositionsRequest {
user: params.user.as_str(),
markets: market_refs.as_deref(),
title: params.title.as_deref(),
event_ids: params.event_id.as_deref(),
limit: params.limit,
offset: params.offset,
sort_by: parsed_sort_by,
sort_direction: parsed_sort_direction,
},
)
.await?;
write_json_output(&positions)?;
Ok(())
}
async fn handle_get_user_activity(
client: &Client,
params: &GetUserActivityArgs,
) -> anyhow::Result<()> {
let market_refs: Option<Vec<&str>> = params
.market
.as_ref()
.map(|m| m.iter().map(|s| s.as_str()).collect());
let parsed_activity_types: Option<Vec<polymarket_hft::client::polymarket::data::ActivityType>> =
params
.activity_type
.as_ref()
.map(|types| {
types
.iter()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::ActivityType>())
.collect::<std::result::Result<Vec<_>, _>>()
})
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --type: {}", e))?;
let parsed_sort_by = params
.sort_by
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::ActivitySortBy>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --sort-by: {}", e))?;
let parsed_sort_direction = params
.sort_direction
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::SortDirection>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --sort-direction: {}", e))?;
let parsed_side = params
.side
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::TradeSide>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --side: {}", e))?;
let activity = client
.get_user_activity(
polymarket_hft::client::polymarket::data::GetUserActivityRequest {
user: params.user.as_str(),
limit: params.limit,
offset: params.offset,
markets: market_refs.as_deref(),
event_ids: params.event_id.as_deref(),
activity_types: parsed_activity_types.as_deref(),
start: params.start,
end: params.end,
sort_by: parsed_sort_by,
sort_direction: parsed_sort_direction,
side: parsed_side,
},
)
.await?;
write_json_output(&activity)?;
Ok(())
}
async fn handle_get_trades(client: &Client, params: &GetTradesArgs) -> anyhow::Result<()> {
let market_refs: Option<Vec<&str>> = params
.market
.as_ref()
.map(|m| m.iter().map(|s| s.as_str()).collect());
let parsed_filter_type = params
.filter_type
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::TradeFilterType>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --filter-type: {}", e))?;
let parsed_side = params
.side
.as_ref()
.map(|s| s.parse::<polymarket_hft::client::polymarket::data::TradeSide>())
.transpose()
.map_err(|e| anyhow::anyhow!("invalid --side: {}", e))?;
let trades = client
.get_trades(polymarket_hft::client::polymarket::data::GetTradesRequest {
limit: params.limit,
offset: params.offset,
taker_only: params.taker_only,
filter_type: parsed_filter_type,
filter_amount: params.filter_amount,
markets: market_refs.as_deref(),
event_ids: params.event_id.as_deref(),
user: params.user.as_deref(),
side: parsed_side,
})
.await?;
write_json_output(&trades)?;
Ok(())
}