Backtesting framework

There seems to be no maintained package for trading backtesting framework like a framewokr in python called backtrader
I did come across

  1. dysonance/Strategems.jl
  2. JuliaQuant/TradingLogic.jl

Anyone in the process of creation or knows of a package that i have not come across ?

3 Likes

Well, we still lack backtesting framework. If you’re interested in rebooting the TradingLogic.jl, feel free to ping me in that repo, just show your blueprint.

2 Likes

I will keep you posted. I am working on a design currently.

4 Likes

hi @bicepjai !

I’m also working on a strictly vectorized single asset & portfolio backtesting framework as well.
Ping me on Julia slack (Mark Aron Szulyovszky) or in DM here (or email or whatever Discourse allows you to do)!

Hi all,

[ANN] Announcing Trading.jl may also be related.

PingPong.jl may also be considered.

Kind regards

2 Likes

SaguaroTrader.jl is a schedule-driven backtesting engine in Julia. We developed it to satisfy our internal use cases, but we welcome any feedback for expanding the capabilities to fit more general use cases.

4 Likes

Hi @tylerjthomas9

Nice to see that qstrader have a Julia version (I used to be a contributor).
I personally find the order_sizer an approach which is a bit complex.
Implementing function like order_market_target

to simply manage orders as a certain target percent with respect to the portfolio value seems a bit simpler (but that’s probably a personal taste)

I wonder how do you manage technical analysis indicators.
Which Julia lib are you using ?
Did you see my post Incremental Technical Analysis indicators about implementing incremental technical analysis indicators ?

2 Likes

It is great to hear from you. I had noticed that you were an active member of the Julia community. Thank you for the suggestion around the order_sizer.

We do not currently do anything with technical analysis indicators. Our internal use case is simply to take a schedule of company weights and build portfolios around that schedule. However, we want to expand the libraries functionality to be more generally useful. I had not seen your post until now, but I will take a look at it.

3 Likes

Some others Julia backtesters to be added to the list.

PS : be also aware of my new package for porfolio analytics via online algorithm Incremental portfolio analytics (OnlinePortfolioAnalytics.jl)

4 Likes

We are having one less Julia backtesting framework… https://github.com/panifie/PingPong.jl
My 7 months late fork is available at GitHub - femtotrader/PingPong.jl: Cryptocurrency trading bot, and backtesting framework in julia
PingPong · Julia Packages

1 Like

What changes did you make to your fork of PingPong.jl?

None. Sometimes I fork on GH because I consider that a project is interesting and I’d prefer to still have this code if it disappears.

1 Like

Lucky.jl has a really interesting architecture. I just wish it were easier to use it with OnlineTechnicalIndicators.jl. I see that you even started an issue asking him about it.

I’ve been thinking about this problem space for a while, and the way he used Rocket.jl to organize his data pipeline feels elegant. He did a lot of things right. Even if I don’t end up using Lucky.jl for my own work, I think I will take a lot of inspiration from it.

1 Like

Hello @g-gundam, OnlineTechnicalIndicators.jl is a great library and I’ve had multiple talks with @FemtoTrader about making one work with the other.

Current Lucky.jl design does not match the way indicators are built in OnlineTechnicalIndicators.jl. I could look again in the future, but I invite you to build a custom operator in Lucky.jl using OnlineTechnicalIndicators.jl. You might find the right way to make both work together.

I’ll gladly answer to any question here or on Github discussions.

1 Like

I’ve been looking at your examples/goldencross.jl, and I know a bit more about Rocket.jl than I did a month ago. I see how I can get OHLC values from source. That’s all I need to start calculating indicator values using OnlineTechnicalIndicators.jl. However, it won’t use the Lucky.jl indicator system (which I don’t understand yet), but it will co-exist alongside it. I think that’s better than nothing, and it’s a start.

It’s not going to be an operator either. I’m going to add another subject to do this. I don’t know if this is the right or wrong way to use Rocket.jl, but it is what’s worked for me so far. You were curious about how I was doing things with Rocket.jl in the other thread, and now you will see.

# I visualized the SMAs your strategy was using,
# so you can see where you're going.
chart_subject = ChartSubject(charts = Dict(
    :daily => Chart(
        "AAPL", Day(1),
        indicators = [
            SMA{Float64}(period=5),
            SMA{Float64}(period=2),
        ],
        visuals = [
            # The periods were so small, 
            # so it didn't look good without LWC_CURVED.
            Dict(
                :line_color => "#f4b860",
                :line_width => 4,
                :line_type  => LWC_CURVED
            ),
            Dict(
                :line_color => "#537ce2",
                :line_width => 2,
                :line_type  => LWC_CURVED
            ),
        ]
    )
))
subscribe!(source, chart_subject)

PS: I feel like your strategy should be executing more trades than it does.

1 Like

Absolutely, it’s better to have something than nothing. A few random comments quickly reading your code, for the conversation and mutual feedback!

If you want to try OnlineTechnicalIndicators with Lucky,

All of the above is about 5 lines to create the Indicator and 15 to have the operator. Should be fast to try if it works! It would be nice though if the operator stuck to the current library conventions : 1/it works on streaming data 2/uses Missing for holes in the output data.

  • I see you hardcoded some types such as EMA{Float64}(period=45)
    Indicators in Lucky have an interface to make it cleaner for anyone to get such types: The interface function is IndicatorType.

  • The Lucky IndicatorType was made to be fully dispatchable. Are SMA{Float64}(period=5)also dispatchable at the period level ? Nothing bad here, simply a design choice versus another.

  • I’m not sure what should subscribe to ChartSubject. It seems like some kind of Blotter to render charts. In which case, I believe integrating with Genie.jl to be the way as both framework are reactive and should work great together out of the box.

-not sure i understand that comment: yield() # INFO: This allows ExchangeFillSubject to have a chance to work.

  • If you indeed need multiple subscribers to ChartSubject, the way is probably to have a single subscriber in your code, and then use the multicast/share/publish operator to have multiple subscribers. (Just for information: Same goes if you want to have multiple input as a single subscription: you can use one of the combineLatest operator from Rocket.jl).

I’ve not had the chance to run your code though, I hope to do soon, at least to look at the nice charts. :slight_smile:

Have a great day.

1 Like

If you want to try OnlineTechnicalIndicators with Lucky, (… explanation omitted …)

Thanks for explaining how you might go about incorporating OnlineTechnicalIndicators into the Lucky indicator system. If I have some down time later, I may explore this. The thing that may slow me down the most at this point is my lack of understanding when it comes to Rocket.jl operators. I have yet to make one or even use one.

Are SMA{Float64}(period=5) also dispatchable at the period level?

I don’t think the period of an indicator is something you can dispatch on with OnlineTechnicalIndicators.jl. (@femtotrader - please correct me if I’m wrong.)

julia> using OnlineTechnicalIndicators

julia> sma50 = SMA{Float64}(period=50)
SMA: n=0 | value=missing

julia> sma200 = SMA{Float64}(period=200)
SMA: n=0 | value=missing

julia> typeof(sma50) == typeof(sma200) # They're the same type.
true

Personally, I’m fine with that.

I’m not sure what should subscribe to ChartSubject. It seems like some kind of Blotter to render charts.

The ChartSubject is a thin wrapper around the Chart struct from my library, TechnicalIndicatorCharts.jl. It’s designed to consume candles and incrementally build a DataFrame that contains OHLCV values + indicator values calculated by OnlineTechnicalIndicators.jl. In my own trading system, I have a StrategySubject that subscribes to the ChartSubject, and it uses the data inside the chart_subject instance to make trading decisions.

Whereas you have an on_next! for each indicator:

Rocket.on_next!(strat::GoldenCross, data::SlowIndicatorType)
Rocket.on_next!(strat::GoldenCross, data::FastIndicatorType)

I have one comparable on_next! that looks like:

Rocket.on_next!(subject::StrategySubject, t::Tuple{Symbol, Candle})

That fires when ChartSubject has formed a complete candle for a Chart. Every complete candle implies all indicators in a Chart had new values calculated for them, so the ChartSubject notifies the StrategySubject with that tuple. Then, the StrategySubject uses the chart data that it already has access to to make trading decisions.

I really don’t want to confuse you further, but if you’re really curious, you can take a peek over at my very experimental code in g-gundam/TradingPipeline.jl.

not sure i understand that comment: yield() # INFO: This allows ExchangeFillSubject to have a chance to work.

I have my own simulation pipeline, and it uses Channels to report back that an order was filled or if a stop loss was hit. The problem was that the Rocket loop was so tight that an order was put in and filled, but the fill response didn’t get reported back until months later in simulated time. The async task that I had monitoring the Channel didn’t get a chance to work until I put that yield there. Then the order fill responses came back in a timely manner.

In realtime situations, the yield wouldn’t have been needed, but in simulation, I needed it. Rocket was too fast.

multicast

Thanks for the tip. I still have a lot to learn about Rocket.

StrategySubject is driven by a state machine that looks like this:

More complex strategies may require more complex state machines, but this is what I’m starting with.

oh sh*t, PingPong.jl is gone! What happened?!

The user who created it is gone too.
https://github.com/panifie

1 Like