Skip to content

Commit

Permalink
Merge pull request #20 from dysonance/femtotrader-julia07
Browse files Browse the repository at this point in the history
Julia Version Updates
  • Loading branch information
dysonance authored Oct 5, 2018
2 parents 133b58f + abda9d8 commit f804047
Show file tree
Hide file tree
Showing 15 changed files with 50 additions and 44 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ language: julia
os:
- linux
julia:
- 0.6
- 0.7
- 1.0
- nightly
matrix:
allow_failures:
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ The key indicator used in this strategy is John Ehlers's MESA Adaptive Moving Av
This strategy simply goes long when the *MAMA* crosses over the *FAMA*, and goes short when the *FAMA* crosses over the *MAMA*. Below is an implementation that shows how to set default arguments to the `Indicators.mama` function and run a simple backtest using those parameters, and also define specified ranges over which we might like to see how the strategy behaves under different parameter sets.

```julia
using Strategems, Temporal, Indicators, Base.Dates
using Strategems, Temporal, Indicators
using Dates

# define universe and gather data
assets = ["CHRIS/CME_CL1", "CHRIS/CME_RB1"]
Expand Down
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
julia 0.6
julia 0.7
Temporal
Indicators
5 changes: 3 additions & 2 deletions examples/faber.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Strategems, Temporal, Indicators, Base.Dates
using Base.Test
using Strategems, Temporal, Indicators
using Dates
using Test

# define universe and gather data
assets = ["EOD/AAPL", "EOD/MCD", "EOD/JPM", "EOD/MMM", "EOD/XOM"]
Expand Down
5 changes: 3 additions & 2 deletions examples/mama.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Strategems, Temporal, Indicators, Base.Dates
using Base.Test
using Strategems, Temporal, Indicators
using Dates
using Test

# define universe and gather data
assets = ["CHRIS/CME_CL1", "CHRIS/CME_RB1"]
Expand Down
2 changes: 1 addition & 1 deletion src/Strategems.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
VERSION >= v"0.6-" && __precompile__(true)

module Strategems
using Base.Dates
using Dates
using Temporal
using Indicators

Expand Down
2 changes: 1 addition & 1 deletion src/indicator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function calculate(indicator::Indicator, input::TS)::TS
return indicator.fun(input; generate_dict(indicator.paramset)...)
end

# function calculate!(indicator::Indicator, input::TS)::Void
# function calculate!(indicator::Indicator, input::TS)::Nothing
# indicator.data = calculate(indicator, input)
# return nothing
# end
Expand Down
11 changes: 5 additions & 6 deletions src/order.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ end
#TODO: add logic whereby the order logic is altered by the type `T` of qty
# (if T<:Int, order that many *shares*, else if T<:Float64, interpret qty as a fraction of the portfolio at time t)

liquidate{T<:Real}(qty::T) = qty
liquidate(qty) = qty

long{T<:Real}(qty::T)::T = qty
buy{T<:Real}(qty::T)::T = qty

short{T<:Real}(qty::T)::T = qty
sell{T<:Real}(qty::T)::T = qty
long(qty) = qty
buy(qty) = qty

short(qty) = qty
sell(qty) = qty
4 changes: 2 additions & 2 deletions src/paramset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function get_n_runs(ps::ParameterSet)::Int
end

function get_param_combos(ps::ParameterSet; n_runs::Int=get_n_runs(ps))::Matrix
combos = Matrix{Any}(n_runs, ps.n_args)
combos = Matrix{Any}(undef, n_runs, ps.n_args)
P = 1
for j in 1:ps.n_args
n_vals = length(ps.arg_ranges[j])
Expand All @@ -38,7 +38,7 @@ function get_param_combos(ps::ParameterSet; n_runs::Int=get_n_runs(ps))::Matrix
last_row = first_row + n_vals*step_by - 1
rows = first_row:step_by:last_row
arg_val = ps.arg_ranges[j][i]
combos[rows,j] = arg_val
combos[rows,j] .= arg_val
end
P *= n_vals
end
Expand Down
2 changes: 1 addition & 1 deletion src/results.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mutable struct Results
optimization::Matrix{Float64}
function Results(trades::Dict{String,TS}=Dict{String,TS}(),
backtest::Dict{String,TS{Float64}}=Dict{String,TS{Float64}}(),
optimization::Matrix{Float64}=Matrix{Float64}(0,0))
optimization::Matrix{Float64}=Matrix{Float64}(undef,0,0))
return new(trades, backtest, optimization)
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/rule.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ macro rule(logic::Expr, args...)
trigger = :($(logic.args[2]))
#action = :($(logic.args[3])$((args...)))
action = :($(logic.args[3]))
args = :($(args...))
args = :($(args))
return esc(:(Rule($trigger, $action, $args)))
end

Expand Down
4 changes: 3 additions & 1 deletion src/signal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ struct Signal
fun::Function
function Signal(switch::Expr)
@assert typeof(eval(switch.args[1])) <: Function
f::Function = eval(switch.args[1])
a::Symbol = switch.args[2]
b::Symbol = switch.args[3]
#pair = eval(switch.args[1])::Function => Tuple{Symbol,Symbol}(switch.args[2]::Symbol, switch.args[3]::Symbol)
Expand All @@ -18,6 +17,9 @@ struct Signal
vec1::Vector = x[a].values[:]
vec2::Vector = x[b].values[:]
comp::Function = eval(switch.args[1])
if comp == ==
comp(x,y) = broadcast(==, x, y)
end
out::BitVector = comp(vec1, vec2)
return out
end
Expand Down
24 changes: 8 additions & 16 deletions src/strategy.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Random

#=
Type definition and methods containing the overarching backtesting object fueling the engine
=#
Expand Down Expand Up @@ -32,7 +34,7 @@ function generate_trades(strat::Strategy; verbose::Bool=true)::Dict{String,TS}
return all_trades
end

function generate_trades!(strat::Strategy; args...)::Void
function generate_trades!(strat::Strategy; args...)::Nothing
strat.results.trades = generate_trades(strat; args...)
return nothing
end
Expand Down Expand Up @@ -81,23 +83,13 @@ function backtest(strat::Strategy; px_trade::Symbol=:Open, px_close::Symbol=:Set
return result
end

function backtest!(strat::Strategy; args...)::Void
function backtest!(strat::Strategy; args...)::Nothing
strat.results.backtest = backtest(strat; args...)
return nothing
end

Base.copy(strat::Strategy) = Strategy(strat.universe, strat.indicator, strat.rules)

# define matrix row iterator protocol
# this allows us to `enumerate(EachRow(M))`
# thereby getting the count of the iteration as well as the row
immutable EachRow{T<:AbstractMatrix}
A::T
end
Base.start(::EachRow) = 1
Base.next(itr::EachRow, s) = (itr.A[s,:], s+1)
Base.done(itr::EachRow, s) = s > size(itr.A,1)

#TODO: more meaningful progres information
#TODO: parallel processing
#TODO: streamline this so that it doesnt run so slow (seems to be recompiling at each run)
Expand All @@ -106,7 +98,7 @@ function optimize(strat::Strategy; samples::Int=0, seed::Int=0, verbose::Bool=tr
n_runs = get_n_runs(strat.indicator.paramset)
idx_samples::Vector{Int} = collect(1:n_runs)
if samples > 0
srand(seed)
Random.seed!(seed)
idx_samples = rand(idx_samples, samples)
else
samples = n_runs
Expand All @@ -126,20 +118,20 @@ function optimize(strat::Strategy; samples::Int=0, seed::Int=0, verbose::Bool=tr
end

# TODO: implement function to edit results member of strat in place
function optimize!(strat::Strategy; samples::Int=0, seed::Int=0, verbose::Bool=true, summary_fun::Function=cum_pnl, args...)::Void
function optimize!(strat::Strategy; samples::Int=0, seed::Int=0, verbose::Bool=true, summary_fun::Function=cum_pnl, args...)::Nothing
n_runs = get_n_runs(strat.indicator.paramset)
idx_samples::Vector{Int} = collect(1:n_runs)
if samples > 0
if seed >= 0
srand(seed)
Random.seed!(seed)
end
idx_samples = rand(idx_samples, samples)
else
samples = n_runs
end
combos = get_param_combos(strat.indicator.paramset, n_runs=n_runs)[idx_samples,:]
strat.results.optimization = zeros(samples,1)
for (run, combo) in enumerate(EachRow(combos))
for (run, combo) in enumerate([combos[i,:] for i in 1:size(combos,1)])
verbose ? println("Run $run/$samples") : nothing
strat.indicator.paramset.arg_defaults = combo
generate_trades!(strat, verbose=false)
Expand Down
8 changes: 4 additions & 4 deletions src/universe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mutable struct Universe
data::Dict{String,TS}
from::TimeType
thru::TimeType
function Universe(assets::Vector{String}, from::TimeType=Base.Dates.Date(0), thru::TimeType=Base.Dates.today())
function Universe(assets::Vector{String}, from::TimeType=Dates.Date(0), thru::TimeType=Dates.today())
@assert assets == unique(assets)
# tickers = guess_tickers(assets)
data = Dict{String,TS}()
Expand All @@ -28,9 +28,9 @@ mutable struct Universe
end

#TODO: ensure type compatibility across variables (specifically with regard to TimeTypes)
function gather!(universe::Universe; source::Function=Temporal.quandl, verbose::Bool=true)::Void
t0 = Vector{Base.Dates.Date}()
tN = Vector{Base.Dates.Date}()
function gather!(universe::Universe; source::Function=Temporal.quandl, verbose::Bool=true)::Nothing
t0 = Vector{Dates.Date}()
tN = Vector{Dates.Date}()
@inbounds for asset in universe.assets
verbose ? print("Sourcing data for asset $asset...") : nothing
indata = source(asset)
Expand Down
17 changes: 13 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Strategems, Temporal, Indicators, Base.Dates
using Base.Test
using Strategems, Temporal, Indicators
using Dates
using Test
using Pkg

# define universe and gather data
assets = ["Corn"]
Expand All @@ -22,7 +24,7 @@ assets = ["Corn"]
# end
# end
# gather!(universe, source=datasource)
gather!(universe, source=(asset)->Temporal.tsread("$(Pkg.dir("Temporal"))/data/$asset.csv"))
gather!(universe, source=(asset)->Temporal.tsread(joinpath(dirname(pathof(Temporal)), "..", "data/$asset.csv")))
@test length(setdiff(assets, collect(keys(universe.data)))) == 0
end
end
Expand All @@ -39,14 +41,18 @@ end
@testset "Indicator" begin
global f(x; args...) = Indicators.mama(x; args...)
global indicator = Indicator(f, paramset)
@test indicator.fun == f
@test indicator.paramset == paramset
end

# define signals that will trigger trading decisions
@testset "Signal" begin
@testset "Construct" begin
global siglong = @signal MAMA FAMA
global sigshort = @signal MAMA FAMA
global sigexit = @signal MAMA .== FAMA
global sigexit = @signal MAMA == FAMA
@test siglong.fun.a == sigshort.fun.a == sigexit.fun.a == :MAMA
@test siglong.fun.b == sigshort.fun.b == sigexit.fun.b == :FAMA
end
end

Expand All @@ -57,6 +63,9 @@ end
global shortrule = @rule sigshort short 100
global exitrule = @rule sigexit liquidate 1.0
global rules = (longrule, shortrule, exitrule)
@test longrule.action == long
@test shortrule.action == short
@test exitrule.action == liquidate
end
end

Expand Down

0 comments on commit f804047

Please sign in to comment.