Esempio n. 1
0
        // Notes:
        //  - A 'Trade' is a description of a trade that *could* be placed. It is different
        //    to an 'Order' which is live on an exchange, waiting to be filled.
        //  - AmountIn * Price does not have to equal AmountOut, because 'Trade' is used
        //    with the order book to calculate the best price for trading a given amount.
        //    The price will represent the best price that covers all of the amount, not
        //    the spot price.
        //  - Don't implicitly change amounts/prices based on order type for the same reason.
        //    Instead, allow any values to be set and use validate to check they're correct.
        //    The 'EditTradeUI' should be used to modify properties and ensure correct behaviour
        //    w.r.t to order type.
        // Rounding issues:
        //  - Quantisation doesn't help, that just makes the discrepancies larger.
        //  - Instead, maintain separate values for amount in and amount out. These imply the
        //    price and allow control over which side of the trade gets rounded.

        /// <summary>Create a trade on 'pair' to convert 'amount_in' of 'coin_in' to 'amount_out'</summary>
        public Trade(Fund fund, TradePair pair, EOrderType order_type, ETradeType trade_type, Unit <decimal> amount_in, Unit <decimal> amount_out, Unit <decimal>?price_q2b = null, string creator = null)
        {
            // Check trade amounts and units
            if (amount_in < 0m._(trade_type.CoinIn(pair)))
            {
                throw new Exception("Invalid trade 'in' amount");
            }
            if (amount_out < 0m._(trade_type.CoinOut(pair)))
            {
                throw new Exception("Invalid trade 'out' amount");
            }
            if (amount_out != 0 && amount_in != 0 && trade_type.PriceQ2B(amount_out / amount_in) < 0m._(pair.RateUnits))
            {
                throw new Exception("Invalid trade price");
            }

            CreatorName = creator ?? string.Empty;
            Fund        = fund;
            Pair        = pair;
            OrderType   = order_type;
            TradeType   = trade_type;
            AmountIn    = amount_in;
            AmountOut   = amount_out;
            PriceQ2B    =
                price_q2b != null ? price_q2b.Value :
                amount_out != 0 && amount_in != 0 ? TradeType.PriceQ2B(amount_out / amount_in) :
                SpotPriceQ2B;
        }
Esempio n. 2
0
        /// <summary>Attempt to make a trade on 'pair' for the given 'price' and base 'amount'</summary>
        private void TryFillOrder(TradePair pair, Fund fund, long order_id, ETradeType tt, EOrderType ot, Unit <decimal> amount_in, Unit <decimal> amount_out, Unit <decimal> remaining_in, out Order ord, out OrderCompleted his)
        {
            // The order can be filled immediately, filled partially, or not filled and remain as an 'Order'.
            // Also, exchanges use the base currency as the amount to fill, so for Q2B trades it's possible
            // that 'amount_in' is less than the trade asked for.
            var market = m_depth[pair];

            // Consume orders
            var price_q2b   = tt.PriceQ2B(amount_out / amount_in);
            var amount_base = tt.AmountBase(price_q2b, amount_in: remaining_in);
            var filled      = market.Consume(pair, tt, ot, price_q2b, amount_base, out var remaining_base);

            // The order is partially or completely filled...
            Debug.Assert(Misc.EqlAmount(amount_base, filled.Sum(x => x.AmountBase) + remaining_base));
            ord = remaining_base != 0 ? new Order(order_id, fund, pair, ot, tt, amount_in, amount_out, tt.AmountIn(remaining_base, price_q2b), Model.UtcNow, Model.UtcNow) : null;
            his = remaining_base != amount_base ? new OrderCompleted(order_id, fund, pair, tt) : null;

            // Add 'TradeCompleted' entries for each order book offer that was filled
            foreach (var fill in filled)
            {
                his.Trades.AddOrUpdate(new TradeCompleted(his.OrderId, ++m_history_id, pair, tt, fill.AmountIn(tt), fill.AmountOut(tt), Exchange.Fee * fill.AmountOut(tt), tt.CoinOut(pair), Model.UtcNow, Model.UtcNow));
            }
        }
Esempio n. 3
0
 /// <summary>The price that the trade was filled at</summary>
 public Unit <decimal> PriceQ2B(ETradeType tt) => tt.PriceQ2B(AmountOut / AmountIn);
Esempio n. 4
0
 /// <summary>Return 'price' in (Quote/Base) for this trade direction. Assumes price is in (CoinOut/CoinIn)</summary>
 public static Unit <decimal> PriceQ2B(this ETradeType tt, Unit <decimal> amount_out, Unit <decimal> amount_in)
 {
     return(tt.PriceQ2B(amount_out / amount_in));
 }