Close RQAlpha P0-P2 parity gaps
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::events::{
|
||||
AccountEvent, FillEvent, OrderEvent, OrderSide, OrderStatus, PositionEvent, ProcessEvent,
|
||||
@@ -69,6 +70,108 @@ pub struct FuturesContractSpec {
|
||||
pub short_margin_rate: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum FuturesCommissionType {
|
||||
ByMoney,
|
||||
ByVolume,
|
||||
}
|
||||
|
||||
impl FuturesCommissionType {
|
||||
pub fn parse(value: &str) -> Self {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"by_volume" | "volume" | "byvolume" => Self::ByVolume,
|
||||
_ => Self::ByMoney,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ByMoney => "by_money",
|
||||
Self::ByVolume => "by_volume",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FuturesTradingParameter {
|
||||
pub symbol: String,
|
||||
pub effective_date: Option<NaiveDate>,
|
||||
pub contract_multiplier: f64,
|
||||
pub long_margin_rate: f64,
|
||||
pub short_margin_rate: f64,
|
||||
pub commission_type: FuturesCommissionType,
|
||||
pub open_commission_ratio: f64,
|
||||
pub close_commission_ratio: f64,
|
||||
pub close_today_commission_ratio: f64,
|
||||
pub price_tick: f64,
|
||||
}
|
||||
|
||||
impl FuturesTradingParameter {
|
||||
pub fn spec(&self) -> FuturesContractSpec {
|
||||
FuturesContractSpec::new(
|
||||
self.contract_multiplier,
|
||||
self.long_margin_rate,
|
||||
self.short_margin_rate,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FuturesTransactionCostModel {
|
||||
pub commission_multiplier: f64,
|
||||
}
|
||||
|
||||
impl Default for FuturesTransactionCostModel {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
commission_multiplier: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FuturesTransactionCostModel {
|
||||
pub fn calculate(
|
||||
&self,
|
||||
params: &FuturesTradingParameter,
|
||||
effect: FuturesPositionEffect,
|
||||
price: f64,
|
||||
quantity: u32,
|
||||
close_today_quantity: u32,
|
||||
) -> f64 {
|
||||
if quantity == 0 || !price.is_finite() || price <= 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
let quantity = quantity as f64;
|
||||
let close_today_quantity = close_today_quantity.min(quantity as u32) as f64;
|
||||
let close_yesterday_quantity = (quantity - close_today_quantity).max(0.0);
|
||||
let raw = match params.commission_type {
|
||||
FuturesCommissionType::ByMoney => match effect {
|
||||
FuturesPositionEffect::Open => {
|
||||
price * quantity * params.contract_multiplier * params.open_commission_ratio
|
||||
}
|
||||
FuturesPositionEffect::Close
|
||||
| FuturesPositionEffect::CloseToday
|
||||
| FuturesPositionEffect::CloseYesterday => {
|
||||
price
|
||||
* params.contract_multiplier
|
||||
* (close_yesterday_quantity * params.close_commission_ratio
|
||||
+ close_today_quantity * params.close_today_commission_ratio)
|
||||
}
|
||||
},
|
||||
FuturesCommissionType::ByVolume => match effect {
|
||||
FuturesPositionEffect::Open => quantity * params.open_commission_ratio,
|
||||
FuturesPositionEffect::Close
|
||||
| FuturesPositionEffect::CloseToday
|
||||
| FuturesPositionEffect::CloseYesterday => {
|
||||
close_yesterday_quantity * params.close_commission_ratio
|
||||
+ close_today_quantity * params.close_today_commission_ratio
|
||||
}
|
||||
},
|
||||
};
|
||||
raw.max(0.0) * self.commission_multiplier.max(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FuturesOrderIntent {
|
||||
pub symbol: String,
|
||||
@@ -78,6 +181,8 @@ pub struct FuturesOrderIntent {
|
||||
pub quantity: u32,
|
||||
pub price: f64,
|
||||
pub transaction_cost: f64,
|
||||
pub limit_price: Option<f64>,
|
||||
pub allow_pending: bool,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
@@ -99,6 +204,8 @@ impl FuturesOrderIntent {
|
||||
quantity,
|
||||
price,
|
||||
transaction_cost,
|
||||
limit_price: None,
|
||||
allow_pending: false,
|
||||
reason: reason.into(),
|
||||
}
|
||||
}
|
||||
@@ -121,10 +228,80 @@ impl FuturesOrderIntent {
|
||||
quantity,
|
||||
price,
|
||||
transaction_cost,
|
||||
limit_price: None,
|
||||
allow_pending: false,
|
||||
reason: reason.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn limit_open(
|
||||
symbol: impl Into<String>,
|
||||
direction: FuturesDirection,
|
||||
spec: FuturesContractSpec,
|
||||
quantity: u32,
|
||||
limit_price: f64,
|
||||
transaction_cost: f64,
|
||||
reason: impl Into<String>,
|
||||
) -> Self {
|
||||
Self::open(
|
||||
symbol,
|
||||
direction,
|
||||
spec,
|
||||
quantity,
|
||||
limit_price,
|
||||
transaction_cost,
|
||||
reason,
|
||||
)
|
||||
.with_limit_price(limit_price)
|
||||
}
|
||||
|
||||
pub fn limit_close(
|
||||
symbol: impl Into<String>,
|
||||
direction: FuturesDirection,
|
||||
effect: FuturesPositionEffect,
|
||||
spec: FuturesContractSpec,
|
||||
quantity: u32,
|
||||
limit_price: f64,
|
||||
transaction_cost: f64,
|
||||
reason: impl Into<String>,
|
||||
) -> Self {
|
||||
Self::close(
|
||||
symbol,
|
||||
direction,
|
||||
effect,
|
||||
spec,
|
||||
quantity,
|
||||
limit_price,
|
||||
transaction_cost,
|
||||
reason,
|
||||
)
|
||||
.with_limit_price(limit_price)
|
||||
}
|
||||
|
||||
pub fn with_limit_price(mut self, limit_price: f64) -> Self {
|
||||
self.limit_price = limit_price
|
||||
.is_finite()
|
||||
.then_some(limit_price)
|
||||
.filter(|v| *v > 0.0);
|
||||
self.allow_pending = self.limit_price.is_some();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allow_pending(mut self, allow_pending: bool) -> Self {
|
||||
self.allow_pending = allow_pending;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_price(mut self, price: f64) -> Self {
|
||||
self.price = price;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_transaction_cost(mut self, transaction_cost: f64) -> Self {
|
||||
self.transaction_cost = transaction_cost;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn side(&self) -> OrderSide {
|
||||
if self.effect == FuturesPositionEffect::Open {
|
||||
self.direction.open_side()
|
||||
@@ -132,6 +309,29 @@ impl FuturesOrderIntent {
|
||||
self.direction.close_side()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_trading_parameter(
|
||||
mut self,
|
||||
params: &FuturesTradingParameter,
|
||||
cost_model: FuturesTransactionCostModel,
|
||||
) -> Self {
|
||||
self.spec = params.spec();
|
||||
if self.transaction_cost <= 0.0 {
|
||||
let close_today_quantity = if self.effect == FuturesPositionEffect::CloseToday {
|
||||
self.quantity
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.transaction_cost = cost_model.calculate(
|
||||
params,
|
||||
self.effect,
|
||||
self.price,
|
||||
self.quantity,
|
||||
close_today_quantity,
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
||||
Reference in New Issue
Block a user