mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-06-15 08:23:51 +00:00
Fixed MORE bugs
This commit is contained in:
parent
46fd55ab8d
commit
b5434fb5de
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@ -19,6 +19,14 @@
|
|||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
"program": "${workspaceFolder}/cmd/sma_crossover.go",
|
"program": "${workspaceFolder}/cmd/sma_crossover.go",
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Run Ichimoku",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/cmd/ichimoku.go",
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -36,7 +36,7 @@ func Backtest(trader *Trader) {
|
|||||||
trader.Tick() // Allow the trader to process the current candlesticks.
|
trader.Tick() // Allow the trader to process the current candlesticks.
|
||||||
broker.Advance() // Give the trader access to the next candlestick.
|
broker.Advance() // Give the trader access to the next candlestick.
|
||||||
}
|
}
|
||||||
trader.closeOrdersAndPositions() // Close any outstanding trades now.
|
trader.CloseOrdersAndPositions() // Close any outstanding trades now.
|
||||||
|
|
||||||
log.Printf("Backtest completed on %d candles. Opening report...\n", trader.Stats().Dated.Len())
|
log.Printf("Backtest completed on %d candles. Opening report...\n", trader.Stats().Dated.Len())
|
||||||
stats := trader.Stats()
|
stats := trader.Stats()
|
||||||
|
@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import auto "github.com/fivemoreminix/autotrader"
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
auto "github.com/fivemoreminix/autotrader"
|
||||||
|
"github.com/fivemoreminix/autotrader/oanda"
|
||||||
|
)
|
||||||
|
|
||||||
type IchimokuStrategy struct {
|
type IchimokuStrategy struct {
|
||||||
convPeriod, basePeriod, leadingPeriods int
|
convPeriod, basePeriod, leadingPeriods int
|
||||||
@ -12,23 +18,63 @@ func (s *IchimokuStrategy) Init(_ *auto.Trader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *IchimokuStrategy) Next(t *auto.Trader) {
|
func (s *IchimokuStrategy) Next(t *auto.Trader) {
|
||||||
ichimoku := auto.Ichimoku(t.Data().Closes(), s.convPeriod, s.basePeriod, s.leadingPeriods)
|
data := t.Data()
|
||||||
time := t.Data().Date(-1)
|
now := *data.Date(-1)
|
||||||
// If the price crosses above the Conversion Line, buy.
|
laggingTime := data.Date(-s.leadingPeriods - 1)
|
||||||
if auto.CrossoverIndex(*time, t.Data().Closes(), ichimoku.Series("Conversion")) {
|
|
||||||
t.Buy(1000)
|
// Extract ichimoku elements
|
||||||
|
ichimoku := auto.Ichimoku(data, s.convPeriod, s.basePeriod, s.leadingPeriods, time.Minute*15)
|
||||||
|
conv := ichimoku.Series("Conversion")
|
||||||
|
base := ichimoku.Series("Base")
|
||||||
|
leadA := ichimoku.Series("LeadingA")
|
||||||
|
leadB := ichimoku.Series("LeadingB")
|
||||||
|
lagging := ichimoku.Series("Lagging")
|
||||||
|
|
||||||
|
// Conditions to buy:
|
||||||
|
// - price closed above the cloud at the current time
|
||||||
|
// - conversion above baseline
|
||||||
|
// - future cloud must be green (LeadingA > LeadingB)
|
||||||
|
|
||||||
|
// Oposite conditions for sell...
|
||||||
|
|
||||||
|
if laggingTime == nil { // Not enough candles to see the lagging.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// If the price crosses below the Conversion Line, sell.
|
|
||||||
if auto.CrossoverIndex(*time, ichimoku.Series("Conversion"), t.Data().Closes()) {
|
if t.IsLong() {
|
||||||
t.Sell(1000)
|
if data.CloseIndex(now) < base.FloatIndex(now) ||
|
||||||
|
leadA.FloatIndex(now) < leadB.FloatIndex(now) {
|
||||||
|
t.CloseOrdersAndPositions()
|
||||||
|
}
|
||||||
|
} else if t.IsShort() {
|
||||||
|
if data.CloseIndex(now) > base.FloatIndex(now) ||
|
||||||
|
leadA.FloatIndex(now) > leadB.FloatIndex(now) {
|
||||||
|
t.CloseOrdersAndPositions()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Look to enter a trade
|
||||||
|
if data.CloseIndex(now) > leadA.FloatIndex(now) &&
|
||||||
|
leadA.FloatIndex(now) > leadB.FloatIndex(now) &&
|
||||||
|
conv.FloatIndex(now) > base.FloatIndex(now) &&
|
||||||
|
leadA.Float(-1) > leadB.Float(-1) &&
|
||||||
|
lagging.FloatIndex(*laggingTime) > leadA.FloatIndex(*laggingTime) {
|
||||||
|
t.Buy(10000, 0, 0)
|
||||||
|
} else if data.CloseIndex(now) < leadA.FloatIndex(now) &&
|
||||||
|
leadA.FloatIndex(now) < leadB.FloatIndex(now) &&
|
||||||
|
conv.FloatIndex(now) < base.FloatIndex(now) &&
|
||||||
|
leadA.Float(-1) < leadB.Float(-1) &&
|
||||||
|
lagging.FloatIndex(*laggingTime) < leadA.FloatIndex(*laggingTime) {
|
||||||
|
t.Sell(10000, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
broker := oanda.NewOandaBroker(os.Getenv("OANDA_TOKEN"), os.Getenv("OANDA_ACCOUNT_ID"), true)
|
||||||
auto.Backtest(auto.NewTrader(auto.TraderConfig{
|
auto.Backtest(auto.NewTrader(auto.TraderConfig{
|
||||||
Broker: auto.NewTestBroker(nil, nil, 10000, 50, 0.0002, 0),
|
Broker: auto.NewTestBroker(broker, nil, 10000, 50, 0.0002, 0),
|
||||||
Strategy: &IchimokuStrategy{convPeriod: 9, basePeriod: 26, leadingPeriods: 52},
|
Strategy: &IchimokuStrategy{convPeriod: 9, basePeriod: 26, leadingPeriods: 52},
|
||||||
Symbol: "EUR_USD",
|
Symbol: "USD_JPY",
|
||||||
Frequency: "M15",
|
Frequency: "M15",
|
||||||
CandlesToKeep: 2500,
|
CandlesToKeep: 2500,
|
||||||
}))
|
}))
|
||||||
|
@ -21,9 +21,9 @@ func (s *SMAStrategy) Next(t *auto.Trader) {
|
|||||||
sma2 := t.Data().Closes().Copy().Rolling(s.period2).Mean()
|
sma2 := t.Data().Closes().Copy().Rolling(s.period2).Mean()
|
||||||
// If the shorter SMA crosses above the longer SMA, buy.
|
// If the shorter SMA crosses above the longer SMA, buy.
|
||||||
if auto.CrossoverIndex(*t.Data().Date(-1), sma1, sma2) {
|
if auto.CrossoverIndex(*t.Data().Date(-1), sma1, sma2) {
|
||||||
t.Buy(1000)
|
t.Buy(1000, 0, 0)
|
||||||
} else if auto.CrossoverIndex(*t.Data().Date(-1), sma2, sma1) {
|
} else if auto.CrossoverIndex(*t.Data().Date(-1), sma2, sma1) {
|
||||||
t.Sell(1000)
|
t.Sell(1000, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ func (f *IndexedFrame[I]) String() string {
|
|||||||
// Print the first ten rows and the last ten rows if the IndexedFrame has more than 20 rows.
|
// Print the first ten rows and the last ten rows if the IndexedFrame has more than 20 rows.
|
||||||
if f.Len() > 20 {
|
if f.Len() > 20 {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
printRow(i, indexes[i])
|
printRow(i+1, indexes[i])
|
||||||
}
|
}
|
||||||
fmt.Fprintf(t, "...\t")
|
fmt.Fprintf(t, "...\t")
|
||||||
for range names {
|
for range names {
|
||||||
@ -136,11 +136,11 @@ func (f *IndexedFrame[I]) String() string {
|
|||||||
}
|
}
|
||||||
fmt.Fprintln(t) // Print new line character.
|
fmt.Fprintln(t) // Print new line character.
|
||||||
for i := 10; i > 0; i-- {
|
for i := 10; i > 0; i-- {
|
||||||
printRow(i, indexes[len(indexes)-i])
|
printRow(len(indexes)-i, indexes[len(indexes)-i])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < f.Len(); i++ {
|
for i := 0; i < f.Len(); i++ {
|
||||||
printRow(i, indexes[i])
|
printRow(i+1, indexes[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,40 +49,21 @@ func RSI(series *FloatSeries, periods int) *FloatSeries {
|
|||||||
// - LeadingA
|
// - LeadingA
|
||||||
// - LeadingB
|
// - LeadingB
|
||||||
// - Lagging
|
// - Lagging
|
||||||
func Ichimoku(series *IndexedSeries[UnixTime], convPeriod, basePeriod, leadingPeriods int) *IndexedFrame[UnixTime] {
|
func Ichimoku(price *IndexedFrame[UnixTime], convPeriod, basePeriod, leadingPeriods int, frequency time.Duration) *IndexedFrame[UnixTime] {
|
||||||
// TODO: make this run concurrently.
|
// TODO: make this run concurrently.
|
||||||
|
|
||||||
// Calculate the Conversion Line.
|
conv := price.Highs().Copy().Rolling(convPeriod).Max().Add(price.Lows().Copy().Rolling(convPeriod).Min()).DivFloat(2)
|
||||||
conv := series.Copy().Rolling(convPeriod).Max().Add(series.Copy().Rolling(convPeriod).Min()).
|
base := price.Highs().Copy().Rolling(basePeriod).Max().Add(price.Lows().Copy().Rolling(basePeriod).Min()).DivFloat(2)
|
||||||
Map(func(_ UnixTime, _ int, val any) any {
|
lagging := price.Closes().Copy()
|
||||||
return val.(float64) / float64(2)
|
leadingA := conv.Copy().Add(base).DivFloat(2)
|
||||||
})
|
leadingB := price.Highs().Copy().Rolling(leadingPeriods).Max().Add(price.Lows().Copy().Rolling(leadingPeriods).Min()).DivFloat(2)
|
||||||
// Calculate the Base Line.
|
|
||||||
base := series.Copy().Rolling(basePeriod).Max().Add(series.Copy().Rolling(basePeriod).Min()).
|
|
||||||
Map(func(_ UnixTime, _ int, val any) any {
|
|
||||||
return val.(float64) / float64(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Calculate the Leading Span A.
|
|
||||||
leadingA := conv.Copy().Rolling(leadingPeriods).Max().Add(base.Copy().Rolling(leadingPeriods).Max()).
|
|
||||||
Map(func(_ UnixTime, _ int, val any) any {
|
|
||||||
return val.(float64) / float64(2)
|
|
||||||
})
|
|
||||||
// Calculate the Leading Span B.
|
|
||||||
leadingB := series.Copy().Rolling(leadingPeriods).Max().Add(series.Copy().Rolling(leadingPeriods).Min()).
|
|
||||||
Map(func(_ UnixTime, _ int, val any) any {
|
|
||||||
return val.(float64) / float64(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Calculate the Lagging Span.
|
|
||||||
lagging := series.Copy().ShiftIndex(-leadingPeriods, UnixTimeStep(time.Hour))
|
|
||||||
|
|
||||||
// Return a DataFrame of the results.
|
// Return a DataFrame of the results.
|
||||||
return NewIndexedFrame(
|
return NewIndexedFrame(
|
||||||
conv.SetName("Conversion"),
|
conv.SetName("Conversion"),
|
||||||
base.SetName("Base"),
|
base.SetName("Base"),
|
||||||
leadingA.SetName("LeadingA"),
|
leadingA.SetName("LeadingA").ShiftIndex(leadingPeriods, UnixTimeStep(frequency)),
|
||||||
leadingB.SetName("LeadingB"),
|
leadingB.SetName("LeadingB").ShiftIndex(leadingPeriods, UnixTimeStep(frequency)),
|
||||||
lagging.SetName("Lagging"),
|
lagging.SetName("Lagging").ShiftIndex(-leadingPeriods, UnixTimeStep(frequency)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -468,7 +468,7 @@ func (s *RollingSeries) Period(row int) []any {
|
|||||||
//
|
//
|
||||||
// Will work with all signed int and float types. Ignores all other values.
|
// Will work with all signed int and float types. Ignores all other values.
|
||||||
func (s *RollingSeries) Max() *Series {
|
func (s *RollingSeries) Max() *Series {
|
||||||
return s.series.Map(func(i int, _ any) any {
|
return s.series.MapReverse(func(i int, _ any) any {
|
||||||
period := s.Period(i)
|
period := s.Period(i)
|
||||||
if len(period) == 0 {
|
if len(period) == 0 {
|
||||||
return 0
|
return 0
|
||||||
@ -514,7 +514,7 @@ func (s *RollingSeries) Max() *Series {
|
|||||||
//
|
//
|
||||||
// Will work with all signed int and float types. Ignores all other values.
|
// Will work with all signed int and float types. Ignores all other values.
|
||||||
func (s *RollingSeries) Min() *Series {
|
func (s *RollingSeries) Min() *Series {
|
||||||
return s.series.Map(func(i int, _ any) any {
|
return s.series.MapReverse(func(i int, _ any) any {
|
||||||
period := s.Period(i)
|
period := s.Period(i)
|
||||||
if len(period) == 0 {
|
if len(period) == 0 {
|
||||||
return 0
|
return 0
|
||||||
|
@ -82,6 +82,17 @@ func (s *IndexedSeries[I]) Add(other *IndexedSeries[I]) *IndexedSeries[I] {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IndexedSeries[I]) AddFloat(num float64) *IndexedSeries[I] {
|
||||||
|
for index, row := range s.index {
|
||||||
|
newValue, err := anymath.Add(s.series.Value(row), num)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error adding values at index %v: %w", index, err))
|
||||||
|
}
|
||||||
|
s.series.SetValue(row, newValue)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// Copy returns a copy of this series.
|
// Copy returns a copy of this series.
|
||||||
func (s *IndexedSeries[I]) Copy() *IndexedSeries[I] {
|
func (s *IndexedSeries[I]) Copy() *IndexedSeries[I] {
|
||||||
return s.CopyRange(0, -1)
|
return s.CopyRange(0, -1)
|
||||||
@ -124,6 +135,17 @@ func (s *IndexedSeries[I]) Div(other *IndexedSeries[I]) *IndexedSeries[I] {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IndexedSeries[I]) DivFloat(num float64) *IndexedSeries[I] {
|
||||||
|
for index, row := range s.index {
|
||||||
|
newValue, err := anymath.Divide(s.series.Value(row), num)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error dividing values at index %v: %w", index, err))
|
||||||
|
}
|
||||||
|
s.series.SetValue(row, newValue)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *IndexedSeries[I]) Filter(f func(i int, val any) bool) *IndexedSeries[I] {
|
func (s *IndexedSeries[I]) Filter(f func(i int, val any) bool) *IndexedSeries[I] {
|
||||||
_ = s.series.Filter(f)
|
_ = s.series.Filter(f)
|
||||||
return s
|
return s
|
||||||
@ -202,6 +224,17 @@ func (s *IndexedSeries[I]) Mul(other *IndexedSeries[I]) *IndexedSeries[I] {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IndexedSeries[I]) MulFloat(num float64) *IndexedSeries[I] {
|
||||||
|
for index, row := range s.index {
|
||||||
|
newValue, err := anymath.Multiply(s.series.Value(row), num)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error multiplying values at index %v: %w", index, err))
|
||||||
|
}
|
||||||
|
s.series.SetValue(row, newValue)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// Name returns the name of the series.
|
// Name returns the name of the series.
|
||||||
func (s *IndexedSeries[I]) Name() string {
|
func (s *IndexedSeries[I]) Name() string {
|
||||||
return s.series.Name()
|
return s.series.Name()
|
||||||
@ -374,6 +407,17 @@ func (s *IndexedSeries[I]) Sub(other *IndexedSeries[I]) *IndexedSeries[I] {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IndexedSeries[I]) SubFloat(num float64) *IndexedSeries[I] {
|
||||||
|
for index, row := range s.index {
|
||||||
|
newValue, err := anymath.Subtract(s.series.Value(row), num)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error subtracting values at index %v: %w", index, err))
|
||||||
|
}
|
||||||
|
s.series.SetValue(row, newValue)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// Value returns the value at the given row.
|
// Value returns the value at the given row.
|
||||||
func (s *IndexedSeries[I]) Value(i int) any {
|
func (s *IndexedSeries[I]) Value(i int) any {
|
||||||
return s.series.Value(i)
|
return s.series.Value(i)
|
||||||
|
@ -140,7 +140,7 @@ func TestRollingSeries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ema5Expected := []float64{1, 1.3333333333333333, 1.8888888888888888, 2.5925925925925926, 3.3950617283950617, 4.395061728395062, 5.395061728395062, 6.395061728395062, 7.395061728395062, 8.395061728395062}
|
ema5Expected := []float64{1, 1.3333333333333333, 1.8888888888888888, 2.5925925925925926, 3.3950617283950617, 4.395061728395062, 5.395061728395062, 6.395061728395062, 7.395061728395062, 8.395061728395062}
|
||||||
ema5 := series.Rolling(5).EMA() // Take the 5 period exponential moving average.
|
ema5 := series.Copy().Rolling(5).EMA() // Take the 5 period exponential moving average.
|
||||||
if ema5.Len() != 10 {
|
if ema5.Len() != 10 {
|
||||||
t.Fatalf("Expected 10 rows, got %d", ema5.Len())
|
t.Fatalf("Expected 10 rows, got %d", ema5.Len())
|
||||||
}
|
}
|
||||||
@ -149,6 +149,29 @@ func TestRollingSeries(t *testing.T) {
|
|||||||
t.Errorf("(%d)\tExpected %f, got %v", i, ema5Expected[i], val)
|
t.Errorf("(%d)\tExpected %f, got %v", i, ema5Expected[i], val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test min and max functions
|
||||||
|
minExpected := []float64{1, 1, 1, 1, 1, 2, 3, 4, 5, 6}
|
||||||
|
min := series.Copy().Rolling(5).Min()
|
||||||
|
if min.Len() != 10 {
|
||||||
|
t.Fatalf("Expected 10 rows, got %d", min.Len())
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if val := min.Float(i); val != minExpected[i] {
|
||||||
|
t.Errorf("(%d)\tExpected %f, got %v", i, minExpected[i], val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxExpected := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
max := series.Copy().Rolling(5).Max()
|
||||||
|
if max.Len() != 10 {
|
||||||
|
t.Fatalf("Expected 10 rows, got %d", max.Len())
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if val := max.Float(i); val != maxExpected[i] {
|
||||||
|
t.Errorf("(%d)\tExpected %f, got %v", i, maxExpected[i], val)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIndexedSeriesInsert(t *testing.T) {
|
func TestIndexedSeriesInsert(t *testing.T) {
|
||||||
@ -239,3 +262,31 @@ func TestIndexedSeries(t *testing.T) {
|
|||||||
t.Errorf("Expected value at index 2018-01-01 to be 1.0, got %v", timeIndexed.ValueIndex(index))
|
t.Errorf("Expected value at index 2018-01-01 to be 1.0, got %v", timeIndexed.ValueIndex(index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIndexedOperations(t *testing.T) {
|
||||||
|
indexed := NewIndexedSeries("test", map[UnixTime]float64{
|
||||||
|
UnixTime(0): 1.0,
|
||||||
|
UnixTime(1): 2.0,
|
||||||
|
UnixTime(2): 3.0,
|
||||||
|
UnixTime(3): 4.0,
|
||||||
|
UnixTime(4): 5.0,
|
||||||
|
})
|
||||||
|
|
||||||
|
added := indexed.Copy().Add(indexed)
|
||||||
|
expected := []float64{2.0, 4.0, 6.0, 8.0, 10.0}
|
||||||
|
if added.Len() != 5 {
|
||||||
|
t.Fatalf("Expected 5 rows, got %d", added.Len())
|
||||||
|
}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if val := added.Float(i); val != expected[i] {
|
||||||
|
t.Errorf("(%d)\tExpected %f, got %v", i, expected[i], val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
added.DivFloat(2.0)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if val := added.Float(i); !EqualApprox(val, indexed.Float(i)) {
|
||||||
|
t.Errorf("(%d)\tExpected %f, got %v", i, indexed.Float(i), val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
34
trader.go
34
trader.go
@ -161,21 +161,21 @@ func (t *Trader) fetchData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trader) Buy(units float64) {
|
func (t *Trader) Buy(units, stopLoss, takeProfit float64) {
|
||||||
t.closeOrdersAndPositions()
|
t.CloseOrdersAndPositions()
|
||||||
t.Log.Printf("Buy %v units", units)
|
t.Log.Printf("Buy %v units", units)
|
||||||
t.Broker.Order(Market, t.Symbol, units, 0, 0, 0)
|
t.Broker.Order(Market, t.Symbol, units, 0, stopLoss, takeProfit)
|
||||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{units, false})
|
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{units, false})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trader) Sell(units float64) {
|
func (t *Trader) Sell(units, stopLoss, takeProfit float64) {
|
||||||
t.closeOrdersAndPositions()
|
t.CloseOrdersAndPositions()
|
||||||
t.Log.Printf("Sell %v units", units)
|
t.Log.Printf("Sell %v units", units)
|
||||||
t.Broker.Order(Market, t.Symbol, -units, 0, 0, 0)
|
t.Broker.Order(Market, t.Symbol, -units, 0, stopLoss, takeProfit)
|
||||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{-units, false})
|
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{-units, false})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trader) closeOrdersAndPositions() {
|
func (t *Trader) CloseOrdersAndPositions() {
|
||||||
for _, order := range t.Broker.OpenOrders() {
|
for _, order := range t.Broker.OpenOrders() {
|
||||||
if order.Symbol() == t.Symbol {
|
if order.Symbol() == t.Symbol {
|
||||||
t.Log.Printf("Cancelling order: %v units", order.Units())
|
t.Log.Printf("Cancelling order: %v units", order.Units())
|
||||||
@ -191,6 +191,26 @@ func (t *Trader) closeOrdersAndPositions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Trader) IsLong() bool {
|
||||||
|
positions := t.Broker.OpenPositions()
|
||||||
|
if len(positions) <= 0 {
|
||||||
|
return false
|
||||||
|
} else if len(positions) > 1 {
|
||||||
|
panic("cannot call IsLong with hedging enabled")
|
||||||
|
}
|
||||||
|
return positions[0].Units() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trader) IsShort() bool {
|
||||||
|
positions := t.Broker.OpenPositions()
|
||||||
|
if len(positions) <= 0 {
|
||||||
|
return false
|
||||||
|
} else if len(positions) > 1 {
|
||||||
|
panic("cannot call IsShort with hedging enabled")
|
||||||
|
}
|
||||||
|
return positions[0].Units() < 0
|
||||||
|
}
|
||||||
|
|
||||||
type TraderConfig struct {
|
type TraderConfig struct {
|
||||||
Broker Broker
|
Broker Broker
|
||||||
Strategy Strategy
|
Strategy Strategy
|
||||||
|
Loading…
x
Reference in New Issue
Block a user