use clap::{Args, Subcommand, ValueEnum};
use color_eyre::eyre::Result;
use polyte_data::DataApi;
use super::SortOrder;
use crate::commands::common::parsing::parse_comma_separated;
use crate::commands::data::trades::TradeSideFilter;
#[derive(Args)]
pub struct PositionsCommand {
#[arg(short, long)]
pub user: String,
#[command(subcommand)]
pub command: PositionsSubcommand,
}
#[derive(Subcommand)]
pub enum PositionsSubcommand {
List {
#[arg(short, long, value_parser = parse_comma_separated)]
market: Option<Vec<String>>,
#[arg(short, long, value_parser = parse_comma_separated)]
event_id: Option<Vec<String>>,
#[arg(long)]
size_threshold: Option<f64>,
#[arg(long)]
redeemable: bool,
#[arg(long)]
mergeable: bool,
#[arg(short, long, default_value = "100")]
limit: u32,
#[arg(short, long, default_value = "0")]
offset: u32,
#[arg(long, value_enum, default_value = "current")]
sort_by: PositionSortField,
#[arg(long, value_enum, default_value = "desc")]
sort_direction: SortOrder,
#[arg(short, long)]
title: Option<String>,
},
Value {
#[arg(short, long, value_parser = parse_comma_separated)]
market: Option<Vec<String>>,
},
Closed {
#[arg(short, long, value_parser = parse_comma_separated)]
market: Option<Vec<String>>,
#[arg(short, long, value_parser = parse_comma_separated)]
event_id: Option<Vec<String>>,
#[arg(short, long)]
title: Option<String>,
#[arg(short, long, default_value = "10")]
limit: u32,
#[arg(short, long, default_value = "0")]
offset: u32,
#[arg(long, value_enum, default_value = "realized-pnl")]
sort_by: ClosedPositionSortField,
#[arg(long, value_enum, default_value = "desc")]
sort_direction: SortOrder,
},
Activity {
#[arg(short, long, value_parser = parse_comma_separated)]
market: Option<Vec<String>>,
#[arg(short, long, value_parser = parse_comma_separated)]
event_id: Option<Vec<String>>,
#[arg(short = 'T', long)]
activity_type: Option<String>,
#[arg(short, long, value_enum)]
side: Option<TradeSideFilter>,
#[arg(long)]
start: Option<i64>,
#[arg(long)]
end: Option<i64>,
#[arg(short, long, default_value = "100")]
limit: u32,
#[arg(short, long, default_value = "0")]
offset: u32,
#[arg(long, value_enum, default_value = "timestamp")]
sort_by: ActivitySortField,
#[arg(long, value_enum, default_value = "desc")]
sort_direction: SortOrder,
},
}
impl PositionsCommand {
pub async fn run(self, data: &DataApi) -> Result<()> {
let positions_api = data.positions(&self.user);
match self.command {
PositionsSubcommand::List {
market,
event_id,
size_threshold,
redeemable,
mergeable,
limit,
offset,
sort_by,
sort_direction,
title,
} => {
let mut request = positions_api.list_positions();
if let Some(ref ids) = market {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.market(ids);
}
if let Some(ref ids) = event_id {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.event_id(ids);
}
if let Some(threshold) = size_threshold {
request = request.size_threshold(threshold);
}
if redeemable {
request = request.redeemable(true);
}
if mergeable {
request = request.mergeable(true);
}
request = request
.limit(limit)
.offset(offset)
.sort_by(sort_by.into())
.sort_direction(sort_direction.into());
if let Some(t) = title {
request = request.title(t);
}
let positions = request.send().await?;
println!("{}", serde_json::to_string_pretty(&positions)?);
}
PositionsSubcommand::Value { market } => {
let mut request = positions_api.positions_value();
if let Some(ref ids) = market {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.market(ids);
}
let value = request.send().await?;
println!("{}", serde_json::to_string_pretty(&value)?);
}
PositionsSubcommand::Closed {
market,
event_id,
title,
limit,
offset,
sort_by,
sort_direction,
} => {
let mut request = positions_api
.closed_positions()
.limit(limit)
.offset(offset)
.sort_by(sort_by.into())
.sort_direction(sort_direction.into());
if let Some(ref ids) = market {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.market(ids);
}
if let Some(ref ids) = event_id {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.event_id(ids);
}
if let Some(t) = title {
request = request.title(t);
}
let positions = request.send().await?;
println!("{}", serde_json::to_string_pretty(&positions)?);
}
PositionsSubcommand::Activity {
market,
event_id,
activity_type,
side,
start,
end,
limit,
offset,
sort_by,
sort_direction,
} => {
let mut request = positions_api
.activity()
.limit(limit)
.offset(offset)
.sort_by(sort_by.into())
.sort_direction(sort_direction.into());
if let Some(ref ids) = market {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.market(ids);
}
if let Some(ref ids) = event_id {
let ids: Vec<&str> = ids.iter().map(|s| s.as_str()).collect();
request = request.event_id(ids);
}
if let Some(types) = activity_type {
let activity_types: Vec<polyte_data::types::ActivityType> = types
.split(',')
.filter_map(|s| match s.trim().to_uppercase().as_str() {
"TRADE" => Some(polyte_data::types::ActivityType::Trade),
"SPLIT" => Some(polyte_data::types::ActivityType::Split),
"MERGE" => Some(polyte_data::types::ActivityType::Merge),
"REDEEM" => Some(polyte_data::types::ActivityType::Redeem),
"REWARD" => Some(polyte_data::types::ActivityType::Reward),
"CONVERSION" => Some(polyte_data::types::ActivityType::Conversion),
_ => None,
})
.collect();
if !activity_types.is_empty() {
request = request.activity_type(activity_types);
}
}
if let Some(s) = side {
request = request.side(s.into());
}
if let Some(ts) = start {
request = request.start(ts);
}
if let Some(ts) = end {
request = request.end(ts);
}
let activity = request.send().await?;
println!("{}", serde_json::to_string_pretty(&activity)?);
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum PositionSortField {
#[default]
Current,
Initial,
Tokens,
CashPnl,
PercentPnl,
Title,
Resolving,
Price,
AvgPrice,
}
impl From<PositionSortField> for polyte_data::types::PositionSortBy {
fn from(field: PositionSortField) -> Self {
match field {
PositionSortField::Current => Self::Current,
PositionSortField::Initial => Self::Initial,
PositionSortField::Tokens => Self::Tokens,
PositionSortField::CashPnl => Self::CashPnl,
PositionSortField::PercentPnl => Self::PercentPnl,
PositionSortField::Title => Self::Title,
PositionSortField::Resolving => Self::Resolving,
PositionSortField::Price => Self::Price,
PositionSortField::AvgPrice => Self::AvgPrice,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum ClosedPositionSortField {
#[default]
RealizedPnl,
Title,
Price,
AvgPrice,
Timestamp,
}
impl From<ClosedPositionSortField> for polyte_data::types::ClosedPositionSortBy {
fn from(field: ClosedPositionSortField) -> Self {
match field {
ClosedPositionSortField::RealizedPnl => Self::RealizedPnl,
ClosedPositionSortField::Title => Self::Title,
ClosedPositionSortField::Price => Self::Price,
ClosedPositionSortField::AvgPrice => Self::AvgPrice,
ClosedPositionSortField::Timestamp => Self::Timestamp,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum ActivitySortField {
#[default]
Timestamp,
Tokens,
Cash,
}
impl From<ActivitySortField> for polyte_data::types::ActivitySortBy {
fn from(field: ActivitySortField) -> Self {
match field {
ActivitySortField::Timestamp => Self::Timestamp,
ActivitySortField::Tokens => Self::Tokens,
ActivitySortField::Cash => Self::Cash,
}
}
}