trades.jl
1 using .Lang: @deassert 2 using Base: negate 3 using .Misc: DFT 4 5 @doc "The quantity of the base currency being exchanged." 6 signedamount(amount, ::AnyBuyOrder) = amount 7 signedamount(amount, ::AnySellOrder) = negate(amount) 8 @doc "The quantity of the quote currency being exchanged." 9 signedsize(size, ::IncreaseOrder) = negate(size) 10 signedsize(size, ::ReduceOrder) = size 11 12 # TODO: abstract `Trade` and implement simple trades and richer trades for margin trading 13 @doc """An order, successfully executed from a strategy request. 14 15 $(FIELDS) 16 17 Entry trades: The date when the order was actually opened, during backtesting, it is usually `date + tf.period` 18 where the timeframe depends on the backtesting `Context`. It should match a candle. 19 Exit trades: It should match the candle when the buy or sell happened. 20 """ 21 struct Trade{O<:OrderType{S} where {S<:OrderSide}, A<:AbstractAsset, E<:ExchangeID, P<:PositionSide} 22 "The order that spawned this trade." 23 order::Order{O,A,E,P} 24 "The date at which the trade (usually its last order) was completed." 25 date::DateTime 26 "The quantity of the base currency being exchanged + (base)fees. 27 NOTE: `amount == value / price - fees_base`, amount should already be (base)fees adjusted. 28 Can be negative." 29 amount::DFT 30 "The actual price (quote currency) of the trade, after slippage." 31 price::DFT 32 "The value of the trade, calculated as `price * amount`." 33 value::DFT 34 "The fees paid for the trade. 35 NOTE: In live mode, fees are exchange dependent, usually (for spot markets) they are in quote (sells), or base (buys) currency. 36 In contracts they are usually in quote or settle currency. 37 Some exchanges handle fees with multiple currencies, in those cases only the base/quote currency values are set in the `trade.fees` field, 38 while the rest should be tracked through the (live updated) balance. 39 Can be negative." 40 fees::DFT 41 "The fees paid for the trade in the base currency." 42 fees_base::DFT 43 "The in/out flow of quote currency, calculated as `value +/- (quote)fees`. 44 NOTE: `size == value + fees` for `IncreaseTrade` and `size == value - fees` for `ReduceTrade`." 45 size::DFT 46 "The leverage the trade was executed with." 47 leverage::DFT 48 "The entry price of the trade." 49 entryprice::DFT 50 function Trade( 51 o::Order{O,A,E,P}; 52 date, 53 amount, 54 price, 55 fees, 56 size, 57 lev=1.0, 58 entryprice=price, 59 fees_base=0.0, 60 ) where {O,A,E,P} 61 @deassert amount > 0.0 62 @deassert size > 0.0 63 @deassert abs(amount) <= abs(o.amount) 64 amount = signedamount(amount, o) 65 value = abs(amount * price) 66 @deassert amount == value / price - fees_base 67 new{O,A,E,P}( 68 o, 69 date, 70 amount, 71 price, 72 value, 73 fees, 74 fees_base, 75 signedsize(size, o), 76 lev, 77 entryprice, 78 ) 79 end 80 end 81 82 @doc "A type representing a trade that opens or adds to a 'long' position in a specific asset" 83 const LongTrade{O,A,E} = Trade{O,A,E,Long} 84 @doc "A type representing a trade that opens or adds to a 'short' position in a specific asset" 85 const ShortTrade{O,A,E} = Trade{O,A,E,Short} 86 @doc "A type representing a buy trade" 87 const BuyTrade{A,E} = Trade{<:OrderType{Buy},A,E,Long} 88 @doc "A type representing a sell trade" 89 const SellTrade{A,E} = Trade{<:OrderType{Sell},A,E,Long} 90 @doc "A type representing a short buy trade" 91 const ShortBuyTrade{A,E} = Trade{<:OrderType{Buy},A,E,Short} 92 @doc "A type representing a short sell trade" 93 const ShortSellTrade{A,E} = Trade{<:OrderType{Sell},A,E,Short} 94 @doc "A type representing an increase trade, which opens or increases the size of a position" 95 const IncreaseTrade{A,E} = Union{BuyTrade{A,E},ShortSellTrade{A,E}} 96 @doc "A type representing a reduce trade, which closes or reduces the size of a position" 97 const ReduceTrade{A,E} = Union{SellTrade{A,E},ShortBuyTrade{A,E}} 98 @doc "A trade type alias with position as parameter" 99 const PositionTrade{P} = Trade{O,A,E,P} where {O<:OrderType,A<:AbstractAsset,E<:ExchangeID} 100 @doc "A type representing a liquidation trade" 101 const LiquidationTrade{S} = Trade{<:LiquidationType{S}} 102 @doc "A type representing a long liquidation trade" 103 const LongLiquidationTrade{S,A,E} = Trade{<:LiquidationType{S},A,E,Long} 104 @doc "A type representing a short liquidation trade" 105 const ShortLiquidationTrade{S,A,E} = Trade{<:LiquidationType{S},A,E,Short} 106 107 exchangeid(::Trade{<:OrderType,<:AbstractAsset,E}) where {E<:ExchangeID} = E 108 function positionside( 109 ::Trade{<:OrderType,<:AbstractAsset,<:ExchangeID,P} 110 ) where {P<:PositionSide} 111 P 112 end 113 function orderside( 114 ::Trade{<:OrderType{S},<:AbstractAsset,<:ExchangeID,<:PositionSide} 115 ) where {S<:OrderSide} 116 S 117 end 118 ordertype(::Trade{O}) where {O<:OrderType} = O 119 islong(o::LongTrade) = true 120 islong(o::ShortTrade) = false 121 isshort(o::LongTrade) = false 122 isshort(o::ShortTrade) = true 123 @doc "Tests if the trade position side is the given position side" 124 ispos(pos::PositionSide, t::Trade) = positionside(t) == pos 125 @doc "Get the fees of a trade." 126 fees(t::Trade) = getfield(t, :fees) + getfield(t, :fees_base) * getfield(t, :price)