utils.jl
1 using .Checks: sanitize_price, sanitize_amount 2 using .Checks: iscost, ismonotonic, SanitizeOff, cost, withfees 3 using .Strategies: PriceTime, universe 4 using .Instances: 5 MarginInstance, NoMarginInstance, AssetInstance, @rprice, @ramount, amount_with_fees 6 using .OrderTypes: 7 IncreaseOrder, ShortBuyOrder, LimitOrderType, MarketOrderType, PostOnlyOrderType 8 using .OrderTypes: ExchangeID, ByPos, ordertype 9 using .Instruments: AbstractAsset 10 using Base: negate, beginsym 11 using .Lang: @lget!, @deassert, @caller 12 using .Misc: Long, Short, PositionSide 13 14 @doc """ Type alias for any limit order """ 15 const AnyLimitOrder{S<:OrderSide,P<:PositionSide} = Order{ 16 <:LimitOrderType{S},<:AbstractAsset,<:ExchangeID,P 17 } 18 19 @doc """ Type alias for any GTC order """ 20 const AnyGTCOrder = Union{GTCOrder,ShortGTCOrder} 21 22 @doc """ Type alias for any FOK order """ 23 const AnyFOKOrder = Union{FOKOrder,ShortFOKOrder} 24 25 @doc """ Type alias for any IOC order """ 26 const AnyIOCOrder = Union{IOCOrder,ShortIOCOrder} 27 28 @doc """ Type alias for any market order """ 29 const AnyMarketOrder{S<:OrderSide,P<:PositionSide} = Order{ 30 <:MarketOrderType{S},<:AbstractAsset,<:ExchangeID,P 31 } 32 33 @doc """ Type alias for any post only order """ 34 const AnyPostOnlyOrder{S<:OrderSide,P<:PositionSide} = Order{ 35 <:PostOnlyOrderType{S},<:AbstractAsset,<:ExchangeID,P 36 } 37 38 @doc """ 39 Clamps the given values within the correct boundaries. 40 41 $(TYPEDSIGNATURES) 42 """ 43 function _doclamp(clamper, ai, whats...) 44 ai = esc(ai) 45 clamper = esc(clamper) 46 expr = quote end 47 for w in whats 48 w = esc(w) 49 push!(expr.args, :(isnothing($w) || begin 50 $w = $clamper($ai, $w) 51 end)) 52 end 53 expr 54 end 55 56 @doc """ 57 Ensures the price is within correct boundaries. 58 59 $(TYPEDSIGNATURES) 60 """ 61 macro price!(ai, prices...) 62 _doclamp(:($(@__MODULE__).sanitize_price), ai, prices...) 63 end 64 65 @doc """ 66 Ensures the amount is within correct boundaries. 67 68 $(TYPEDSIGNATURES) 69 """ 70 macro amount!(ai, amounts...) 71 _doclamp(:($(@__MODULE__).sanitize_amount), ai, amounts...) 72 end 73 74 @doc """ 75 Calculates the commitment for an increase order without margin. 76 77 $(TYPEDSIGNATURES) 78 """ 79 function committment( 80 ::Type{<:IncreaseOrder}, ai::NoMarginInstance, price, amount; kwargs... 81 ) 82 @deassert amount > 0.0 83 withfees(cost(price, amount), maxfees(ai), IncreaseOrder) 84 end 85 86 @doc """ 87 Calculates the commitment for a leveraged position. 88 89 $(TYPEDSIGNATURES) 90 """ 91 function committment( 92 o::Type{<:IncreaseOrder}, 93 ai::MarginInstance, 94 price, 95 amount; 96 ntl=cost(price, amount), 97 fees=ntl * maxfees(ai), 98 lev=leverage(ai, positionside(o)()), 99 kwargs..., 100 ) 101 margin = ntl / lev 102 margin + fees 103 end 104 105 @doc """ 106 Calculates the commitment when exiting a position for longs. 107 108 $(TYPEDSIGNATURES) 109 """ 110 function committment(::Type{<:SellOrder}, ai, price, amount; fees_base=0.0, kwargs...) 111 @deassert amount > 0.0 112 amount_with_fees(amount, fees_base) 113 end 114 115 @doc """ 116 Calculates the commitment when exiting a position for shorts. 117 118 $(TYPEDSIGNATURES) 119 """ 120 function committment(::Type{<:ShortBuyOrder}, ai, price, amount; fees_base=0.0, kwargs...) 121 @deassert amount > 0.0 122 amount_with_fees(negate(amount), fees_base) 123 end 124 125 @doc """ 126 Calculates the partial commitment of a trade. 127 128 $(TYPEDSIGNATURES) 129 """ 130 function committment(ai::AssetInstance, t::Trade) 131 o = t.order 132 committment( 133 typeof(o), ai, o.price, t.amount; t.fees_base, t.fees, ntl=t.value, lev=t.leverage 134 ) 135 end 136 137 @doc """ 138 Calculates the commitment for an order. 139 140 $(TYPEDSIGNATURES) 141 """ 142 function committment(ai::AssetInstance, o::Order; kwargs...) 143 committment(typeof(o), ai, o.price, o.amount; kwargs...) 144 end 145 146 @doc """ 147 Calculates the unfulfilled amount for a buy order. 148 149 $(TYPEDSIGNATURES) 150 """ 151 function unfillment(t::Type{<:AnyBuyOrder}, amount) 152 @deassert amount > 0.0 153 @deassert !(t isa AnySellOrder) 154 negate(abs(amount)) 155 end 156 157 @doc """ 158 Calculates the unfulfilled amount for a sell order. 159 160 $(TYPEDSIGNATURES) 161 """ 162 function unfillment(t::Type{<:AnySellOrder}, amount) 163 @deassert amount > 0.0 164 @deassert !(t isa AnyBuyOrder) 165 amount 166 end 167 168 @doc """ 169 Calculates the unfulfilled amount for an order. 170 171 $(TYPEDSIGNATURES) 172 """ 173 unfillment(o::Order) = unfillment(typeof(o), o.amount) 174 175 @doc """ 176 Checks if a strategy can commit to an increase order. 177 178 $(TYPEDSIGNATURES) 179 """ 180 function iscommittable(s::Strategy, ::Type{<:IncreaseOrder}, commit, ai) 181 @deassert st.freecash(s) |> gtxzero 182 c = st.freecash(s) 183 comm = commit[] 184 c >= comm || isapprox(c, comm) 185 end 186 187 @doc """ 188 Checks if a strategy can commit to a sell order. 189 190 $(TYPEDSIGNATURES) 191 """ 192 function iscommittable(s::Strategy, ::Type{<:SellOrder}, commit, ai) 193 @deassert Instances.freecash(ai, Long()) |> gtxzero 194 @deassert commit[] |> gtxzero 195 c = Instances.freecash(ai, Long()) 196 comm = commit[] 197 c >= comm || isapprox(c, comm) 198 end 199 200 @doc """ 201 Checks if a strategy can commit to a short buy order. 202 203 $(TYPEDSIGNATURES) 204 """ 205 function iscommittable(::Strategy, ::Type{<:ShortBuyOrder}, commit, ai) 206 @deassert Instances.freecash(ai, Short()) |> ltxzero 207 @deassert commit[] |> ltxzero 208 c = Instances.freecash(ai, Short()) 209 comm = commit[] 210 c <= comm || isapprox(c, comm) 211 end 212 213 @doc """ 214 Iterates over all the orders in a strategy. 215 216 $(TYPEDSIGNATURES) 217 """ 218 function orders(s::Strategy) 219 OrderIterator((orders(s, ai, side) for side in (Buy, Sell) for ai in s.holdings)) 220 end 221 222 @doc """ 223 Iterates over all the orderless orders in a strategy. 224 225 $(TYPEDSIGNATURES) 226 """ 227 function orders(s::Strategy, ::Val{:orderless}) 228 (o for side in (Buy, Sell) for ai in s.holdings for o in orders(s, ai, side)) 229 end 230 231 function orders(s::Strategy, ::BySide{O}, ::Val{:orderless}) where {O<:Union{Buy,Sell}} 232 odict = ordersdict(s, O) 233 (o for ai in s.holdings for o in odict[ai]) 234 end 235 236 @doc """ 237 Iterates over all the orders in a strategy (all the assets in the universe). 238 239 $(TYPEDSIGNATURES) 240 """ 241 function orders(s::Strategy, ::Val{:universe}) 242 OrderIterator((orders(s, ai, side) for side in (Buy, Sell) for ai in s.universe)) 243 end 244 245 @doc """ 246 Iterates orderlessly over all the orders in a strategy (all the assets in the universe). 247 248 $(TYPEDSIGNATURES) 249 """ 250 function orders(s::Strategy, ::Val{:orderless}, ::Val{:universe}) 251 OrderIterator((orders(s, ai, side) for side in (Buy, Sell) for ai in s.universe)) 252 end 253 254 @doc """ 255 Iterates over all the orders for an asset instance in a strategy. 256 257 $(TYPEDSIGNATURES) 258 """ 259 function orders(s::Strategy, ai::AssetInstance) 260 buys = orders(s, ai, Buy) 261 if length(buys) == 0 262 orders(s, ai, Sell) 263 else 264 sells = orders(s, ai, Sell) 265 if length(sells) == 0 266 buys 267 else 268 OrderIterator(buys, sells) 269 end 270 end 271 end 272 273 @doc """ 274 Iterates over all the orderless orders for an asset instance in a strategy. 275 276 $(TYPEDSIGNATURES) 277 """ 278 function orders(s::Strategy, ai::AssetInstance, ::Val{:orderless}) 279 (o for side in (Buy, Sell) for o in orders(s, ai, side)) 280 end 281 282 @doc """ 283 Returns all orders for an asset instance in a strategy. 284 285 $(TYPEDSIGNATURES) 286 """ 287 orders(s, ai, ::Type{BuyOrSell}) = orders(s, ai) 288 289 @doc """ 290 Returns all buy orders for a strategy. 291 292 $(TYPEDSIGNATURES) 293 """ 294 orders(s::Strategy, ::BySide{Buy}) = 295 OrderIterator(Iterators.flatten(pairs(dict) for dict in values(s.buyorders))) 296 297 @doc """ 298 Returns all sell orders for a strategy. 299 300 $(TYPEDSIGNATURES) 301 """ 302 orders(s::Strategy, ::BySide{Sell}) = 303 OrderIterator(Iterators.flatten(pairs(dict) for dict in values(s.sellorders))) 304 305 orders(s::Strategy, ::Type{BuyOrSell}) = orders(s) 306 307 @doc """ 308 Returns all buy orders for an asset in a strategy. 309 310 $(TYPEDSIGNATURES) 311 """ 312 function orders(s::Strategy{M,S,E}, ai, ::BySide{Buy}) where {M,S,E} 313 @lget! s.buyorders ai st.BuyOrdersDict{E}(st.BuyPriceTimeOrdering()) 314 end 315 316 @doc """ 317 Returns all sell orders for an asset in a strategy. 318 319 $(TYPEDSIGNATURES) 320 """ 321 function orders(s::Strategy{M,S,E}, ai, ::BySide{Sell}) where {M,S,E} 322 @lget! s.sellorders ai st.SellOrdersDict{E}(st.SellPriceTimeOrdering()) 323 end 324 325 """ 326 Returns a unique list of orders from the trade history of a given asset instance. 327 328 $(TYPEDSIGNATURES) 329 """ 330 function ordershistory(ai::AssetInstance) 331 unique(t.order for t in trades(ai)) 332 end 333 334 @doc """ 335 Returns all keys for orders in a strategy. 336 337 $(TYPEDSIGNATURES) 338 """ 339 Base.keys(s::Strategy, args...; kwargs...) = (k for (k, _) in orders(s, args...; kwargs...)) 340 341 @doc """ 342 Returns all values for orders in a strategy. 343 344 $(TYPEDSIGNATURES) 345 """ 346 function Base.values(s::Strategy, args...; kwargs...) 347 (o for (_, o) in orders(s, args...; kwargs...)) 348 end 349 350 @doc """ 351 Returns the first order for an asset in a strategy. 352 353 $(TYPEDSIGNATURES) 354 """ 355 function Base.first(s::Strategy{M,S,E}, ai, bs::BySide=BuyOrSell) where {M,S,E} 356 values(s, ai, bs) |> first 357 end 358 359 @doc """ 360 Returns the first index for an order for an asset in a strategy. 361 362 $(TYPEDSIGNATURES) 363 """ 364 function Base.firstindex(s::Strategy{M,S,E}, ai, bs::BySide=BuyOrSell) where {M,S,E} 365 keys(s, ai, bs) |> first 366 end 367 368 @doc """ 369 Returns the last order for an asset in a strategy. 370 371 $(TYPEDSIGNATURES) 372 """ 373 function Base.last(s::Strategy{M,S,E}, ai, bs::BySide=BuyOrSell) where {M,S,E} 374 ans = missing 375 for v in values(s, ai, bs) 376 ans = v 377 end 378 ismissing(ans) && throw(BoundsError()) 379 ans 380 end 381 382 @doc """ 383 Returns the last index for an order for an asset in a strategy. 384 385 $(TYPEDSIGNATURES) 386 """ 387 function Base.lastindex(s::Strategy{M,S,E}, ai, bs::BySide=BuyOrSell) where {M,S,E} 388 ans = missing 389 for k in keys(s, ai, bs) 390 ans = k 391 end 392 ismissing(ans) && throw(BoundsError()) 393 ans 394 end 395 396 function ordersdict(s::Strategy, bs::BySide{O}) where {O<:Union{Buy,Sell}} 397 bs === Buy ? s.buyorders : s.sellorders 398 end 399 400 @doc """ 401 Returns the count of orders in a strategy. 402 403 $(TYPEDSIGNATURES) 404 """ 405 function orderscount(s::Strategy, ::BySide{O}) where {O} 406 ans = 0 407 foreach(ordersdict(s, O)) do (_, dict) 408 ans += length(dict) 409 end 410 ans 411 end 412 413 @doc """ 414 Returns the count of pending entry orders in a strategy. 415 416 $(TYPEDSIGNATURES) 417 """ 418 function orderscount(s::Strategy, ::Val{:increase}) 419 ans = 0 420 itr = values(s) 421 foreach(itr) do o 422 if o isa IncreaseOrder 423 ans += 1 424 end 425 end 426 ans 427 end 428 429 @doc """ 430 Returns the count of pending exit orders in a strategy. 431 432 $(TYPEDSIGNATURES) 433 """ 434 function orderscount(s::Strategy, ::Val{:reduce}) 435 ans = 0 436 itr = values(s) 437 foreach(itr) do o 438 if o isa ReduceOrder 439 ans += 1 440 end 441 end 442 ans 443 end 444 445 function orderscount(s::Strategy, ::Val{:inc_red}) 446 inc_ans = 0 447 dec_ans = 0 448 itr = values(s) 449 foreach(itr) do o 450 if o isa IncreaseOrder 451 inc_ans += 1 452 elseif o isa ReduceOrder 453 dec_ans += 1 454 end 455 end 456 return inc_ans, dec_ans 457 end 458 459 @doc """ 460 Returns the total count of pending orders in a strategy. 461 462 $(TYPEDSIGNATURES) 463 """ 464 function orderscount(s::Strategy) 465 orderscount(s, Buy) + orderscount(s, Sell) 466 end 467 468 @doc """ 469 Returns the count of orders for an asset in a strategy. 470 471 $(TYPEDSIGNATURES) 472 """ 473 function orderscount(s::Strategy, ai::AssetInstance) 474 n = 0 475 foreach(orders(s, ai)) do _ 476 n += 1 477 end 478 n 479 end 480 481 @doc """ 482 Returns the count of orders for an asset in a strategy. 483 484 $(TYPEDSIGNATURES) 485 """ 486 orderscount(s::Strategy, ai::AssetInstance, ::Type{BuyOrSell}) = orderscount(s, ai) 487 488 @doc """ 489 Returns the count of buy orders for an asset in a strategy. 490 491 $(TYPEDSIGNATURES) 492 """ 493 orderscount(s::Strategy, ai::AssetInstance, ::Type{Buy}) = length(buyorders(s, ai)) 494 495 @doc """ 496 Returns the count of sell orders for an asset in a strategy. 497 498 $(TYPEDSIGNATURES) 499 """ 500 orderscount(s::Strategy, ai::AssetInstance, ::Type{Sell}) = length(sellorders(s, ai)) 501 502 @doc """Checks if any of the holdings has non dust cash. 503 504 $(TYPEDSIGNATURES) 505 """ 506 function hascash(s::Strategy) 507 for ai in s.holdings 508 iszero(ai) || return true 509 end 510 return false 511 end 512 513 @doc """ 514 Checks if a strategy has orders. 515 516 $(TYPEDSIGNATURES) 517 """ 518 hasorders(s::Strategy) = orderscount(s) > 0 519 520 @doc """ 521 Returns buy orders for an asset in a strategy. 522 523 $(TYPEDSIGNATURES) 524 """ 525 buyorders(s::Strategy, ai) = orders(s, ai, Buy) 526 527 @doc """ 528 Returns sell orders for an asset in a strategy. 529 530 $(TYPEDSIGNATURES) 531 """ 532 sellorders(s::Strategy, ai) = orders(s, ai, Sell) 533 534 @doc """ 535 Returns orders for an asset in a strategy by side. 536 537 $(TYPEDSIGNATURES) 538 """ 539 sideorders(s::Strategy, ai, ::Type{Buy}) = buyorders(s, ai) 540 541 @doc """ 542 Returns orders for an asset in a strategy by side. 543 544 $(TYPEDSIGNATURES) 545 """ 546 sideorders(s::Strategy, ai, ::Type{Sell}) = sellorders(s, ai) 547 548 @doc """ 549 Returns orders for an asset in a strategy by side. 550 551 $(TYPEDSIGNATURES) 552 """ 553 sideorders(s::Strategy, ai, ::BySide{S}) where {S} = sideorders(s, ai, S) 554 555 @doc """ 556 Checks if an asset instance has pending buy orders in a strategy. 557 558 $(TYPEDSIGNATURES) 559 """ 560 hasorders(s::Strategy, ai, ::Type{S}) where {S<:Union{Buy,Sell}} = length(orders(s, ai, S)) > 0 561 562 @doc """ 563 Checks if an asset instance has pending orders in a strategy. 564 565 $(TYPEDSIGNATURES) 566 """ 567 function hasorders(s::Strategy, ai::AssetInstance) 568 hasorders(s, ai, Sell) || hasorders(s, ai, Buy) 569 end 570 571 function hasorders(s::Strategy, ai::AssetInstance, ::Type{BuyOrSell}) 572 hasorders(s, ai) 573 end 574 575 @doc """ 576 Checks if an asset instance has a specific order in a strategy by side. 577 578 $(TYPEDSIGNATURES) 579 """ 580 function hasorders(s::Strategy, ai, id::String, ::BySide{S}=BuyOrSell) where {S<:OrderSide} 581 for o in values(s, ai, S) 582 o.id == id && return true 583 end 584 false 585 end 586 587 @doc """ 588 Checks if a strategy has a specific order for an asset. 589 590 $(TYPEDSIGNATURES) 591 """ 592 Base.haskey(s::Strategy, ai, o::Order) = haskey(sideorders(s, ai, o), pricetime(o)) 593 594 @doc """ 595 Checks if a strategy has a specific order for an asset by price and time. 596 597 $(TYPEDSIGNATURES) 598 """ 599 function Base.haskey(s::Strategy, ai, pt::PriceTime, side::BySide{<:Union{Buy,Sell}}) 600 haskey(sideorders(s, ai, side), pt) 601 end 602 603 @doc """ 604 Checks if a strategy has a specific order for an asset by price and time. 605 606 $(TYPEDSIGNATURES) 607 """ 608 function Base.haskey(s::Strategy, ai, pt::PriceTime, ::BySide{BuyOrSell}) 609 haskey(sideorders(s, ai, Buy), pt) || haskey(sideorders(s, ai, Sell), pt) 610 end 611 612 @doc """ 613 Checks if a strategy has a specific order for an asset by price and time. 614 615 $(TYPEDSIGNATURES) 616 """ 617 Base.haskey(s::Strategy, ai, pt::PriceTime) = haskey(s, ai, pt, BuyOrSell) 618 619 @doc """ 620 Checks if a strategy has sell orders. 621 622 $(TYPEDSIGNATURES) 623 """ 624 function hasorders(s::Strategy, ::BySide{S}) where {S<:OrderSide} 625 for ai in universe(s) 626 ords = sideorders(s, ai, S) 627 if !isempty(ords) 628 return true 629 end 630 end 631 return false 632 end 633 634 @doc """ 635 Checks if a strategy is out of orders. 636 637 $(TYPEDSIGNATURES) 638 """ 639 function isoutof_orders(s::Strategy) 640 ltxzero(s.cash) && isempty(s.holdings) && orderscount(s) == 0 641 end 642 643 @doc """ 644 Checks a buy trade. 645 646 $(TYPEDSIGNATURES) 647 """ 648 function _check_trade(t::BuyTrade, ai) 649 @deassert t.price <= t.order.price || ordertype(t) <: MarketOrderType 650 @deassert t.size < 0.0 651 @deassert t.amount > 0.0 652 @deassert if isshort(t) 653 ltxzero(ai, committed(t.order), Val(:amount)) 654 else 655 gtxzero(committed(t.order), atol=fees(t)) 656 end committed(t.order), t.order 657 end 658 659 @doc """ 660 Checks a sell trade. 661 662 $(TYPEDSIGNATURES) 663 """ 664 function _check_trade(t::SellTrade, ai) 665 @deassert t.price >= t.order.price || ordertype(t) <: MarketOrderType ( 666 t.price, t.order.price 667 ) 668 @deassert t.size > 0.0 669 @deassert t.amount < 0.0 670 @deassert committed(t.order) >= -1e-12 671 end 672 673 @doc """ 674 Checks a short sell trade. 675 676 $(TYPEDSIGNATURES) 677 """ 678 function _check_trade(t::ShortSellTrade, ai) 679 @deassert t.price >= t.order.price || ordertype(t) <: MarketOrderType 680 @deassert t.size < 0.0 681 @deassert t.amount < 0.0 682 @deassert abs(committed(t.order)) <= t.fees || t.order isa ShortSellOrder 683 end 684 685 @doc """ 686 Checks a short buy trade. 687 688 $(TYPEDSIGNATURES) 689 """ 690 function _check_trade(t::ShortBuyTrade, ai) 691 @deassert t.price <= t.order.price || ordertype(t) <: MarketOrderType ( 692 t.price, t.order.price 693 ) 694 @deassert t.size > 0.0 695 @deassert t.amount > 0.0 696 @deassert committed(t.order) |> ltxzero 697 end 698 699 @doc """ 700 Checks the cash for an asset instance in a strategy for long. 701 702 $(TYPEDSIGNATURES) 703 """ 704 function _check_cash(ai::AssetInstance, ::Long) 705 @deassert gtxzero(ai, committed(ai, Long()), Val(:amount)) || 706 ordertype(last(ai.history)) <: MarketOrderType committed(ai, Long()).value 707 @deassert cash(ai, Long()) |> gtxzero 708 end 709 710 @doc """ 711 Checks the cash for an asset instance in a strategy for short. 712 713 $(TYPEDSIGNATURES) 714 """ 715 _check_cash(ai::AssetInstance, ::Short) = begin 716 @deassert committed(ai, Short()) |> ltxzero 717 @deassert cash(ai, Short()) |> ltxzero 718 end 719 720 _cur_by_side(o::BuyOrder) = :fees_base 721 _cur_by_side(o::SellOrder) = :fees 722 @doc """ 723 The sum of all the trades fees that have heppened for the order. 724 725 $(TYPEDSIGNATURES) 726 """ 727 function feespaid(o::Order) 728 ot = trades(o) 729 if isempty(ot) 730 0.0 731 else 732 cur = _cur_by_side(o) 733 sum(getproperty(t, cur) for t in trades(o)) 734 end 735 end 736 737 tradetuple(t::Trade) = (t.order, t.price, t.size, t.amount) 738 function tradetuple(ai::AssetInstance, t::Trade) 739 ( 740 t.order.id, 741 t.order.date, 742 toprecision(t.price, ai.precision.price), 743 toprecision(t.size, ai.precision.amount), 744 toprecision(t.amount, ai.precision.amount), 745 ) 746 end 747 748 @doc """ 749 Check if the given trade is in the order. 750 751 $(TYPEDSIGNATURES) 752 """ 753 hastrade(o::Order, t::Trade) = begin 754 tup = tradetuple(t) 755 for t in trades(o) 756 if tup == tradetuple(t) 757 return true 758 end 759 end 760 return false 761 end 762 763 @doc "More precise version of `hastrade`." 764 function hastrade(ai::AssetInstance, o::Order, t::Trade) 765 tup = tradetuple(ai, t) 766 for t in trades(o) 767 if tup == tradetuple(ai, t) 768 return true 769 end 770 end 771 return false 772 end 773 774 @doc """ 775 Returns the order that matches the given id (if any). 776 777 $(TYPEDSIGNATURES) 778 """ 779 function order_byid(s::Strategy, ai::AssetInstance, id::String) 780 for o in values(s, ai) 781 if o.id == id 782 return o 783 end 784 end 785 end 786 787 function order_byid(s::Strategy, id::String) 788 for ai in s.holdings 789 o = order_byid(s, ai, id) 790 if !isnothing(o) 791 return o 792 end 793 end 794 end 795 796 isnocost(o::BySide) = ordertype(o) <: MarketOrderType