Better signal formatting

This commit is contained in:
2026-08-02 00:19:47 +02:00
parent 51998aad68
commit 05cf081339
3 changed files with 117 additions and 118 deletions
+80 -78
View File
@@ -32,89 +32,91 @@ impl Engine {
return Ok(()); return Ok(());
}; };
let order_ids = self.risk_engine.create_order().await?; let order_ids = self.risk_engine.create_order(signal).await?;
let order = BatchOrder { println!("{order_ids:?}");
orders: vec![
OrderRequest {
asset: asset_id,
is_buy: matches!(signal.side, Side::Bid),
limit_px: signal.entry_price,
sz: 0.into(),
reduce_only: false,
order_type: OrderTypePlacement::Limit {
tif: TimeInForce::Gtc,
},
cloid: order_ids.entry,
},
OrderRequest {
asset: asset_id,
is_buy: matches!(signal.side, Side::Bid),
limit_px: signal.entry_price,
sz: 0.into(),
reduce_only: true,
order_type: OrderTypePlacement::Trigger {
is_market: true,
trigger_px: signal.take_profit,
tpsl: hypercore::TpSl::Tp,
},
cloid: order_ids.take_profit,
},
OrderRequest {
asset: asset_id,
is_buy: matches!(signal.side, Side::Bid),
limit_px: signal.entry_price,
sz: 0.into(),
reduce_only: true,
order_type: OrderTypePlacement::Trigger {
is_market: true,
trigger_px: signal.stop_loss,
tpsl: hypercore::TpSl::Sl,
},
cloid: order_ids.stop_loss,
},
],
grouping: hypercore::OrderGrouping::Na,
builder: None,
};
let nonce = chrono::Utc::now().timestamp_millis() as u64; // let order = BatchOrder {
// orders: vec![
// OrderRequest {
// asset: asset_id,
// is_buy: matches!(signal.side, Side::Bid),
// limit_px: signal.entry_price,
// sz: 0.into(),
// reduce_only: false,
// order_type: OrderTypePlacement::Limit {
// tif: TimeInForce::Gtc,
// },
// cloid: order_ids.entry,
// },
// OrderRequest {
// asset: asset_id,
// is_buy: matches!(signal.side, Side::Bid),
// limit_px: signal.entry_price,
// sz: 0.into(),
// reduce_only: true,
// order_type: OrderTypePlacement::Trigger {
// is_market: true,
// trigger_px: signal.take_profit,
// tpsl: hypercore::TpSl::Tp,
// },
// cloid: order_ids.take_profit,
// },
// OrderRequest {
// asset: asset_id,
// is_buy: matches!(signal.side, Side::Bid),
// limit_px: signal.entry_price,
// sz: 0.into(),
// reduce_only: true,
// order_type: OrderTypePlacement::Trigger {
// is_market: true,
// trigger_px: signal.stop_loss,
// tpsl: hypercore::TpSl::Sl,
// },
// cloid: order_ids.stop_loss,
// },
// ],
// grouping: hypercore::OrderGrouping::Na,
// builder: None,
// };
match client // let nonce = chrono::Utc::now().timestamp_millis() as u64;
.place(&acc.private_key.0, order, nonce, None, None)
.await
{
Ok(o) if o.iter().any(|o| matches!(o, OrderResponseStatus::Error(_))) => {
self.risk_engine.order_placed(order_ids).await;
}
Ok(e) => { // match client
self.terminal_server // .place(&acc.private_key.0, order, nonce, None, None)
.error( // .await
"self::order", // {
&format!( // Ok(o) if o.iter().any(|o| matches!(o, OrderResponseStatus::Error(_))) => {
"order rejected: {}", // self.risk_engine.order_placed(order_ids).await;
e.into_iter() // }
.filter_map(|res| {
if let OrderResponseStatus::Error(e) = res {
Some(e)
} else {
None
}
})
.collect::<Vec<_>>()
.join(", ")
),
)
.await?;
}
Err(e) => { // Ok(e) => {
self.terminal_server // self.terminal_server
.error("self::order", &e.to_string()) // .error(
.await?; // "self::order",
} // &format!(
} // "order rejected: {}",
// e.into_iter()
// .filter_map(|res| {
// if let OrderResponseStatus::Error(e) = res {
// Some(e)
// } else {
// None
// }
// })
// .collect::<Vec<_>>()
// .join(", ")
// ),
// )
// .await?;
// }
// Err(e) => {
// self.terminal_server
// .error("self::order", &e.to_string())
// .await?;
// }
// }
} else { } else {
self.terminal_server self.terminal_server
.error("Engine::order", "Unable to get active account") .error("Engine::order", "Unable to get active account")
+20 -22
View File
@@ -14,22 +14,13 @@ use tokio::sync::Mutex;
use crate::{engine::Engine, store::config::RiskConfig}; use crate::{engine::Engine, store::config::RiskConfig};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct OrderIds { pub struct EngineOrder {
pub risk_equity: Decimal,
pub size: Decimal,
pub entry: Cloid, pub entry: Cloid,
pub take_profit: Cloid, pub take_profit: Cloid,
pub stop_loss: Cloid, pub stop_loss: Cloid,
pub risk_equity: Decimal,
}
impl OrderIds {
pub fn new(risk_equity: Decimal) -> Self {
Self {
entry: Cloid::random(),
take_profit: Cloid::random(),
stop_loss: Cloid::random(),
risk_equity,
}
}
} }
pub struct RiskState { pub struct RiskState {
@@ -40,7 +31,7 @@ pub struct RiskState {
pub struct RiskEngine { pub struct RiskEngine {
// Orders made by the engine // Orders made by the engine
pub orders: Mutex<Vec<OrderIds>>, pub orders: Mutex<Vec<EngineOrder>>,
pub handle: Mutex<ConnectionHandle>, pub handle: Mutex<ConnectionHandle>,
pub state: Mutex<RiskState>, pub state: Mutex<RiskState>,
pub engine: Weak<Engine>, pub engine: Weak<Engine>,
@@ -154,18 +145,25 @@ impl RiskEngine {
Ok(under_max_losses) Ok(under_max_losses)
} }
pub async fn create_order(&self) -> tokio::io::Result<OrderIds> { pub async fn create_order(&self, signal: &Signal) -> tokio::io::Result<EngineOrder> {
let state = self.state.lock().await; let state = self.state.lock().await;
Ok(OrderIds::new( let risk = self
self.get_risk_config() .get_risk_config()
.await? .await?
.risk_per_trade .risk_per_trade
.get(state.starting_equity), .get(state.starting_equity);
))
Ok(EngineOrder {
entry: Cloid::random(),
take_profit: Cloid::random(),
stop_loss: Cloid::random(),
risk_equity: risk,
size: risk / (signal.entry_price - signal.stop_loss).abs(),
})
} }
pub async fn order_placed(&self, order: OrderIds) { pub async fn order_placed(&self, order: EngineOrder) {
self.orders.lock().await.push(order); self.orders.lock().await.push(order);
} }
+17 -18
View File
@@ -54,31 +54,22 @@ impl Formatted for EventLog {
impl Formatted for SignalStatus { impl Formatted for SignalStatus {
fn get_formatted(&self) -> Vec<String> { fn get_formatted(&self) -> Vec<String> {
match self { match self {
Ok(signal) => { Ok(signal) | Err(signal) => {
vec![ vec![
format!("\x1b[33mOK\x1b[0m"), if matches!(self, Ok(_)) {
if matches!(signal.side, Side::Ask) { format!("\x1b[34mOK\x1b[0m")
} else {
format!("\x1b[31mREJ\x1b[0m")
},
if matches!(signal.side, Side::Bid) {
format!("\x1b[32mBUY\x1b[0m") format!("\x1b[32mBUY\x1b[0m")
} else { } else {
format!("\x1b[31mSELL\x1b[0m") format!("\x1b[31mSELL\x1b[0m")
}, },
format_symbol(&signal.symbol), format_symbol(&signal.symbol),
format_usd(signal.entry_price.as_f64()), format_usd(signal.entry_price.as_f64()),
format!("\x1b[33mAPR {}\x1b[0m", signal.confidence), format_usd(signal.take_profit.as_f64()),
] format_usd_reverse(signal.stop_loss.as_f64()),
}
Err(signal) => {
vec![
format!("\x1b[31mERR\x1b[0m"),
if matches!(signal.side, Side::Ask) {
format!("\x1b[32mBUY\x1b[0m")
} else {
format!("\x1b[31mSELL\x1b[0m")
},
format_symbol(&signal.symbol),
format_usd(signal.entry_price.as_f64()),
format!("\x1b[33mAPR {}\x1b[0m", signal.confidence),
] ]
} }
} }
@@ -274,6 +265,14 @@ pub fn format_usd(value: f64) -> String {
} }
} }
pub fn format_usd_reverse(value: f64) -> String {
if value.is_sign_positive() {
format!("\x1b[31m${}\x1b[0m", format_f64(value))
} else {
format!("\x1b[32m${}\x1b[0m", format_f64(value))
}
}
pub fn format_f64(value: f64) -> String { pub fn format_f64(value: f64) -> String {
let abs = value.abs(); let abs = value.abs();