mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-06-15 00:13:51 +00:00
Implemented stop and limit orders
This commit is contained in:
parent
56740b5a00
commit
d851061d1f
118
backtesting.go
118
backtesting.go
@ -371,6 +371,28 @@ func (b *TestBroker) Advance() {
|
||||
func (b *TestBroker) Tick() {
|
||||
// Check if the current candle's high and lows contain any take profits or stop losses.
|
||||
high, low := b.Data.High(b.CandleIndex()), b.Data.Low(b.CandleIndex())
|
||||
|
||||
// Update orders.
|
||||
for _, any_o := range b.orders {
|
||||
if any_o.Fulfilled() {
|
||||
continue
|
||||
}
|
||||
o := any_o.(*TestOrder)
|
||||
|
||||
if o.orderType == Limit {
|
||||
if o.price >= low && o.price <= high {
|
||||
o.fulfill(o.price)
|
||||
}
|
||||
} else if o.orderType == Stop {
|
||||
if o.price <= high && o.price >= low {
|
||||
o.fulfill(o.price)
|
||||
}
|
||||
} else {
|
||||
panic("the order type is either unknown or otherwise should not be market because those are fulfilled immediately")
|
||||
}
|
||||
}
|
||||
|
||||
// Update positions.
|
||||
for _, any_p := range b.positions {
|
||||
if any_p.Closed() {
|
||||
continue
|
||||
@ -385,15 +407,18 @@ func (b *TestBroker) Tick() {
|
||||
// Check if the position should be closed.
|
||||
if p.takeProfit > 0 {
|
||||
if (p.units > 0 && p.takeProfit <= high) || (p.units < 0 && p.takeProfit >= low) {
|
||||
p.close(p.takeProfit, closeTypeTakeProfit)
|
||||
p.close(p.takeProfit, CloseTakeProfit)
|
||||
continue
|
||||
}
|
||||
} else if p.stopLoss > 0 {
|
||||
}
|
||||
// stopLoss won't be set if trailingSL is set, and vice versa.
|
||||
if p.stopLoss > 0 {
|
||||
if (p.units > 0 && p.stopLoss >= low) || (p.units < 0 && p.stopLoss <= high) {
|
||||
p.close(p.stopLoss, closeTypeStopLoss)
|
||||
p.close(p.stopLoss, CloseStopLoss)
|
||||
}
|
||||
} else if p.trailingSL > 0 {
|
||||
if (p.units > 0 && p.trailingSL >= low) || (p.units < 0 && p.trailingSL <= high) {
|
||||
p.close(p.trailingSL, closeTypeTrailingStop)
|
||||
p.close(p.trailingSL, CloseTrailingStop)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -438,7 +463,7 @@ func (b *TestBroker) Candles(symbol string, frequency string, count int) (*DataF
|
||||
return b.Data.Copy(start, adjCount).(*DataFrame), nil
|
||||
}
|
||||
|
||||
func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takeProfit float64) (Order, error) {
|
||||
func (b *TestBroker) Order(orderType OrderType, symbol string, units, price, stopLoss, takeProfit float64) (Order, error) {
|
||||
if units == 0 {
|
||||
return nil, ErrZeroUnits
|
||||
}
|
||||
@ -452,17 +477,18 @@ func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takePro
|
||||
}
|
||||
}
|
||||
|
||||
price := b.Price("", units > 0)
|
||||
|
||||
slippage := rand.Float64() * b.Slippage * price
|
||||
price += slippage - slippage/2 // Get a slippage as +/- 50% of the slippage.
|
||||
|
||||
var trailingSL float64
|
||||
if stopLoss < 0 {
|
||||
trailingSL = -stopLoss
|
||||
}
|
||||
|
||||
marketPrice := b.Price("", units > 0)
|
||||
if orderType == Market {
|
||||
price = marketPrice
|
||||
}
|
||||
|
||||
order := &TestOrder{
|
||||
broker: b,
|
||||
id: strconv.Itoa(rand.Int()),
|
||||
leverage: b.Leverage,
|
||||
position: nil,
|
||||
@ -470,7 +496,7 @@ func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takePro
|
||||
symbol: symbol,
|
||||
takeProfit: takeProfit,
|
||||
time: time.Now(),
|
||||
orderType: MarketOrder,
|
||||
orderType: orderType,
|
||||
units: units,
|
||||
}
|
||||
if trailingSL > 0 {
|
||||
@ -479,27 +505,18 @@ func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takePro
|
||||
order.stopLoss = stopLoss
|
||||
}
|
||||
|
||||
// Instantly fulfill the order.
|
||||
order.position = &TestPosition{
|
||||
broker: b,
|
||||
closed: false,
|
||||
entryPrice: price,
|
||||
id: strconv.Itoa(rand.Int()),
|
||||
leverage: b.Leverage,
|
||||
symbol: symbol,
|
||||
takeProfit: takeProfit,
|
||||
time: time.Now(),
|
||||
units: units,
|
||||
// TODO: only instantly fulfill market orders or sometimes limit orders when requirements are met.
|
||||
if orderType == Market {
|
||||
order.fulfill(price)
|
||||
} else if orderType == Limit {
|
||||
if units > 0 && marketPrice <= order.price {
|
||||
order.fulfill(price)
|
||||
} else if units < 0 && marketPrice >= order.price {
|
||||
order.fulfill(price)
|
||||
}
|
||||
}
|
||||
if trailingSL > 0 {
|
||||
order.position.trailingSLDist = trailingSL
|
||||
} else {
|
||||
order.position.stopLoss = stopLoss
|
||||
}
|
||||
b.Cash -= order.position.EntryValue()
|
||||
|
||||
b.orders = append(b.orders, order)
|
||||
b.positions = append(b.positions, order.position)
|
||||
b.SignalEmit("OrderPlaced", order)
|
||||
|
||||
return order, nil
|
||||
@ -556,8 +573,8 @@ type TestPosition struct {
|
||||
broker *TestBroker
|
||||
closed bool
|
||||
entryPrice float64
|
||||
closePrice float64 // If zero, then position has not been closed.
|
||||
closeType string // SL, TS, TP
|
||||
closePrice float64 // If zero, then position has not been closed.
|
||||
closeType OrderCloseType // SL, TS, TP
|
||||
id string
|
||||
leverage float64
|
||||
symbol string
|
||||
@ -570,18 +587,11 @@ type TestPosition struct {
|
||||
}
|
||||
|
||||
func (p *TestPosition) Close() error {
|
||||
p.close(p.broker.Price("", p.units < 0), closeTypeMarket)
|
||||
p.close(p.broker.Price("", p.units < 0), CloseMarket)
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
closeTypeMarket = "M"
|
||||
closeTypeStopLoss = "SL"
|
||||
closeTypeTrailingStop = "TS"
|
||||
closeTypeTakeProfit = "TP"
|
||||
)
|
||||
|
||||
func (p *TestPosition) close(atPrice float64, closeType string) {
|
||||
func (p *TestPosition) close(atPrice float64, closeType OrderCloseType) {
|
||||
if p.closed {
|
||||
return
|
||||
}
|
||||
@ -597,7 +607,7 @@ func (p *TestPosition) Closed() bool {
|
||||
return p.closed
|
||||
}
|
||||
|
||||
func (p *TestPosition) CloseType() string {
|
||||
func (p *TestPosition) CloseType() OrderCloseType {
|
||||
return p.closeType
|
||||
}
|
||||
|
||||
@ -657,6 +667,7 @@ func (p *TestPosition) Value() float64 {
|
||||
}
|
||||
|
||||
type TestOrder struct {
|
||||
broker *TestBroker
|
||||
id string
|
||||
leverage float64
|
||||
position *TestPosition
|
||||
@ -674,6 +685,31 @@ func (o *TestOrder) Cancel() error {
|
||||
return ErrCancelFailed
|
||||
}
|
||||
|
||||
func (o *TestOrder) fulfill(atPrice float64) {
|
||||
slippage := rand.Float64() * o.broker.Slippage * atPrice
|
||||
atPrice += slippage - slippage/2 // Adjust price as +/- 50% of the slippage.
|
||||
|
||||
o.position = &TestPosition{
|
||||
broker: o.broker,
|
||||
closed: false,
|
||||
entryPrice: atPrice,
|
||||
id: strconv.Itoa(rand.Int()),
|
||||
leverage: o.leverage,
|
||||
symbol: o.symbol,
|
||||
takeProfit: o.takeProfit,
|
||||
time: time.Now(),
|
||||
units: o.units,
|
||||
}
|
||||
if o.trailingSL > 0 {
|
||||
o.position.trailingSLDist = o.trailingSL
|
||||
} else {
|
||||
o.position.stopLoss = o.stopLoss
|
||||
}
|
||||
o.broker.Cash -= o.position.EntryValue()
|
||||
|
||||
o.broker.positions = append(o.broker.positions, o.position)
|
||||
}
|
||||
|
||||
func (o *TestOrder) Fulfilled() bool {
|
||||
return o.position != nil
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ const testDataCSV = `date,open,high,low,close,volume
|
||||
2022-01-01,1.1,1.2,1.0,1.15,100
|
||||
2022-01-02,1.15,1.2,1.1,1.2,110
|
||||
2022-01-03,1.2,1.3,1.15,1.25,120
|
||||
2022-01-04,1.25,1.3,1.2,1.1,130
|
||||
2022-01-04,1.25,1.3,1.0,1.1,130
|
||||
2022-01-05,1.1,1.2,1.0,1.15,110
|
||||
2022-01-06,1.15,1.2,1.1,1.2,120
|
||||
2022-01-07,1.2,1.3,1.15,1.25,140
|
||||
@ -79,21 +79,13 @@ func TestBacktestingBrokerCandles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktestingBrokerFunctions(t *testing.T) {
|
||||
broker := NewTestBroker(nil, nil, 100_000, 20, 0, 0)
|
||||
|
||||
if !EqualApprox(broker.NAV(), 100_000) {
|
||||
t.Errorf("Expected NAV to be 100_000, got %f", broker.NAV())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktestingBrokerOrders(t *testing.T) {
|
||||
func TestBacktestingBrokerMarketOrders(t *testing.T) {
|
||||
data := newTestingDataframe()
|
||||
broker := NewTestBroker(nil, data, 100_000, 50, 0, 0)
|
||||
broker.Slippage = 0
|
||||
|
||||
timeBeforeOrder := time.Now()
|
||||
order, err := broker.MarketOrder("EUR_USD", 50_000, 0, 0) // Buy 50,000 USD for 1000 EUR with no stop loss or take profit
|
||||
order, err := broker.Order(Market, "EUR_USD", 50_000, 0, 0, 0) // Buy 50,000 USD for 1000 EUR with no stop loss or take profit
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -125,7 +117,7 @@ func TestBacktestingBrokerOrders(t *testing.T) {
|
||||
if order.TakeProfit() != 0 {
|
||||
t.Errorf("Expected take profit to be 0, got %f", order.TakeProfit())
|
||||
}
|
||||
if order.Type() != MarketOrder {
|
||||
if order.Type() != Market {
|
||||
t.Errorf("Expected order type to be MarketOrder, got %s", order.Type())
|
||||
}
|
||||
|
||||
@ -181,12 +173,111 @@ func TestBacktestingBrokerOrders(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktestingBrokerStopLimitOrders(t *testing.T) {
|
||||
func TestBacktestingBrokerLimitOrders(t *testing.T) {
|
||||
data := newTestingDataframe()
|
||||
broker := NewTestBroker(nil, data, 100_000, 50, 0, 0)
|
||||
broker.Slippage = 0
|
||||
|
||||
order, err := broker.MarketOrder("", 10_000, 1.05, 1.25)
|
||||
order, err := broker.Order(Limit, "EUR_USD", -50_000, 1.3, 1.35, 1.1) // Sell limit 50,000 USD for 1000 EUR
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if order == nil {
|
||||
t.Fatal("Order is nil")
|
||||
}
|
||||
if order.Price() != 1.3 {
|
||||
t.Errorf("Expected order price to be 1.3, got %f", order.Price())
|
||||
}
|
||||
if order.Fulfilled() != false {
|
||||
t.Error("Expected order to not be fulfilled")
|
||||
}
|
||||
|
||||
broker.Advance()
|
||||
broker.Advance() // Advance to the third candle where the order should be fulfilled
|
||||
|
||||
if order.Fulfilled() != true {
|
||||
t.Error("Expected order to be fulfilled")
|
||||
}
|
||||
|
||||
position := order.Position()
|
||||
if position == nil {
|
||||
t.Fatal("Position is nil")
|
||||
}
|
||||
if position.Closed() != false {
|
||||
t.Fatal("Expected position to not be closed")
|
||||
}
|
||||
|
||||
broker.Advance() // Advance to the fourth candle which should hit our take profit
|
||||
|
||||
if position.Closed() != true {
|
||||
t.Fatal("Expected position to be closed")
|
||||
}
|
||||
if position.ClosePrice() != 1.1 {
|
||||
t.Errorf("Expected position close price to be 1.1, got %f", position.ClosePrice())
|
||||
}
|
||||
if position.CloseType() != CloseTakeProfit {
|
||||
t.Errorf("Expected position close type to be TP, got %s", position.CloseType())
|
||||
}
|
||||
if !EqualApprox(position.PL(), 10_000) { // abs(1.1-1.3) * 50_000 = 10,000
|
||||
t.Errorf("Expected position PL to be 10000, got %f", position.PL())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktestingBrokerStopOrders(t *testing.T) {
|
||||
data := newTestingDataframe()
|
||||
broker := NewTestBroker(nil, data, 100_000, 50, 0, 0)
|
||||
broker.Slippage = 0
|
||||
|
||||
order, err := broker.Order(Stop, "EUR_USD", 50_000, 1.2, 1, 1.3) // Buy stop 50,000 EUR for 1000 USD
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if order == nil {
|
||||
t.Fatal("Order is nil")
|
||||
}
|
||||
if order.Price() != 1.2 {
|
||||
t.Errorf("Expected order price to be 1.2, got %f", order.Price())
|
||||
}
|
||||
if order.Fulfilled() != false {
|
||||
t.Error("Expected order to not be fulfilled")
|
||||
}
|
||||
|
||||
broker.Advance() // Advance to the second candle where the order should be fulfilled
|
||||
|
||||
if order.Fulfilled() != true {
|
||||
t.Error("Expected order to be fulfilled")
|
||||
}
|
||||
|
||||
position := order.Position()
|
||||
if position == nil {
|
||||
t.Fatal("Position is nil")
|
||||
}
|
||||
if position.Closed() != false {
|
||||
t.Fatal("Expected position to not be closed")
|
||||
}
|
||||
|
||||
broker.Advance() // Advance to the third candle which should hit our take profit
|
||||
|
||||
if position.Closed() != true {
|
||||
t.Fatal("Expected position to be closed")
|
||||
}
|
||||
if position.ClosePrice() != 1.3 {
|
||||
t.Errorf("Expected position close price to be 1.3, got %f", position.ClosePrice())
|
||||
}
|
||||
if position.CloseType() != CloseTakeProfit {
|
||||
t.Errorf("Expected position close type to be TP, got %s", position.CloseType())
|
||||
}
|
||||
if !EqualApprox(position.PL(), 5000) { // (1.3-1.2) * 50_000 = 5000
|
||||
t.Errorf("Expected position PL to be 5000, got %f", position.PL())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktestingBrokerStopLossTakeProfit(t *testing.T) {
|
||||
data := newTestingDataframe()
|
||||
broker := NewTestBroker(nil, data, 100_000, 50, 0, 0)
|
||||
broker.Slippage = 0
|
||||
|
||||
order, err := broker.Order(Market, "", 10_000, 0, 1.05, 1.25)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -226,7 +317,7 @@ func TestBacktestingBrokerStopLimitOrders(t *testing.T) {
|
||||
|
||||
broker.Advance() // 4th candle
|
||||
|
||||
order, err = broker.MarketOrder("", 10_000, -0.2, 1.4) // Long position with trailing stop loss of 0.2.
|
||||
order, err = broker.Order(Market, "", 10_000, 0, -0.2, 1.4) // Long position with trailing stop loss of 0.2.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -271,7 +362,7 @@ func TestBacktestingBrokerStopLimitOrders(t *testing.T) {
|
||||
if !EqualApprox(position.PL(), -500) { // (1.05-1.1) * 10_000 = -500
|
||||
t.Errorf("Expected position PL to be 1000, got %f", position.PL())
|
||||
}
|
||||
if position.CloseType() != closeTypeTrailingStop {
|
||||
t.Errorf("Expected close type to be %q, got %q", closeTypeTrailingStop, position.CloseType())
|
||||
if position.CloseType() != CloseTrailingStop {
|
||||
t.Errorf("Expected close type to be %q, got %q", CloseTrailingStop, position.CloseType())
|
||||
}
|
||||
}
|
||||
|
51
broker.go
51
broker.go
@ -5,12 +5,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type OrderCloseType string
|
||||
|
||||
const (
|
||||
CloseMarket OrderCloseType = "M"
|
||||
CloseStopLoss OrderCloseType = "SL"
|
||||
CloseTrailingStop OrderCloseType = "TS"
|
||||
CloseTakeProfit OrderCloseType = "TP"
|
||||
)
|
||||
|
||||
type OrderType string
|
||||
|
||||
const (
|
||||
MarketOrder OrderType = "MARKET"
|
||||
LimitOrder OrderType = "LIMIT"
|
||||
StopOrder OrderType = "STOP"
|
||||
Market OrderType = "MARKET" // Market means to buy or sell at the current market price, which may not be what you ask for.
|
||||
Limit OrderType = "LIMIT" // Limit means to buy or sell at a specific price or better.
|
||||
Stop OrderType = "STOP" // Stop means to buy or sell when the price reaches a specific price or worse.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -37,22 +46,22 @@ type Order interface {
|
||||
}
|
||||
|
||||
type Position interface {
|
||||
Close() error // Close attempts to close the position and returns an error if it fails. If the error is nil, the position was closed.
|
||||
Closed() bool // Closed returns true if the position has been closed with the broker.
|
||||
CloseType() string // CloseType returns the type of order used to close the position.
|
||||
ClosePrice() float64 // ClosePrice returns the price of the symbol at the time the position was closed. May be zero if the position is still open.
|
||||
EntryPrice() float64 // EntryPrice returns the price of the symbol at the time the position was opened.
|
||||
EntryValue() float64 // EntryValue returns the value of the position at the time it was opened.
|
||||
Id() string // Id returns the unique identifier of the position by the broker.
|
||||
Leverage() float64 // Leverage returns the leverage of the position.
|
||||
PL() float64 // PL returns the profit or loss of the position.
|
||||
Symbol() string // Symbol returns the symbol name of the position.
|
||||
TrailingStop() float64 // TrailingStop returns the trailing stop loss price of the position.
|
||||
StopLoss() float64 // StopLoss returns the stop loss price of the position.
|
||||
TakeProfit() float64 // TakeProfit returns the take profit price of the position.
|
||||
Time() time.Time // Time returns the time the position was opened.
|
||||
Units() float64 // Units returns the number of units purchased or sold by the position.
|
||||
Value() float64 // Value returns the value of the position at the current price.
|
||||
Close() error // Close attempts to close the position and returns an error if it fails. If the error is nil, the position was closed.
|
||||
Closed() bool // Closed returns true if the position has been closed with the broker.
|
||||
CloseType() OrderCloseType // CloseType returns the type of order used to close the position.
|
||||
ClosePrice() float64 // ClosePrice returns the price of the symbol at the time the position was closed. May be zero if the position is still open.
|
||||
EntryPrice() float64 // EntryPrice returns the price of the symbol at the time the position was opened.
|
||||
EntryValue() float64 // EntryValue returns the value of the position at the time it was opened.
|
||||
Id() string // Id returns the unique identifier of the position by the broker.
|
||||
Leverage() float64 // Leverage returns the leverage of the position.
|
||||
PL() float64 // PL returns the profit or loss of the position.
|
||||
Symbol() string // Symbol returns the symbol name of the position.
|
||||
TrailingStop() float64 // TrailingStop returns the trailing stop loss price of the position.
|
||||
StopLoss() float64 // StopLoss returns the stop loss price of the position.
|
||||
TakeProfit() float64 // TakeProfit returns the take profit price of the position.
|
||||
Time() time.Time // Time returns the time the position was opened.
|
||||
Units() float64 // Units returns the number of units purchased or sold by the position.
|
||||
Value() float64 // Value returns the value of the position at the current price.
|
||||
}
|
||||
|
||||
// Broker is an interface that defines the methods that a broker must implement to report symbol data and place orders, etc. All Broker implementations must also implement the Signaler interface and emit the following functions when necessary:
|
||||
@ -65,8 +74,8 @@ type Broker interface {
|
||||
Ask(symbol string) float64 // Ask returns the buy price of the symbol, which is typically higher than the sell price.
|
||||
// Candles returns a dataframe of candles for the given symbol, frequency, and count by querying the broker.
|
||||
Candles(symbol, frequency string, count int) (*DataFrame, error)
|
||||
// MarketOrder places a market order for the given symbol and returns an error if it fails. A short position has negative units. If stopLoss or takeProfit are zero, they will not be set. If stopLoss is greater than the current price for a long position or less than the current price for a short position, the order will fail. Likewise for takeProfit. If the stopLoss is a negative number, it is used as a trailing stop loss to represent how many price points away the stop loss should be from the current price.
|
||||
MarketOrder(symbol string, units, stopLoss, takeProfit float64) (Order, error)
|
||||
// Order places an order with orderType for the given symbol and returns an error if it fails. A short position has negative units. If the orderType is Market, the price argument will be ignored and the order will be fulfilled at current price. Otherwise, price is used to set the target price for Stop and Limit orders. If stopLoss or takeProfit are zero, they will not be set. If the stopLoss is greater than the current price for a long position or less than the current price for a short position, the order will fail. Likewise for takeProfit. If the stopLoss is a negative number, it is used as a trailing stop loss to represent how many price points away the stop loss should be from the current price.
|
||||
Order(orderType OrderType, symbol string, units, price, stopLoss, takeProfit float64) (Order, error)
|
||||
NAV() float64 // NAV returns the net asset value of the account.
|
||||
PL() float64 // PL returns the profit or loss of the account.
|
||||
OpenOrders() []Order
|
||||
|
@ -82,7 +82,7 @@ func (b *OandaBroker) Candles(symbol, frequency string, count int) (*auto.DataFr
|
||||
return newDataframe(candlestickResponse)
|
||||
}
|
||||
|
||||
func (b *OandaBroker) MarketOrder(symbol string, units, stopLoss, takeProfit float64) (auto.Order, error) {
|
||||
func (b *OandaBroker) Order(orderType auto.OrderType, symbol string, units, price, stopLoss, takeProfit float64) (auto.Order, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -165,14 +165,14 @@ func (t *Trader) fetchData() {
|
||||
func (t *Trader) Buy(units float64) {
|
||||
t.closeOrdersAndPositions()
|
||||
t.Log.Printf("Buy %f units", units)
|
||||
t.Broker.MarketOrder(t.Symbol, units, 0.0, 0.0)
|
||||
t.Broker.Order(Market, t.Symbol, units, 0, 0, 0)
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{units, false})
|
||||
}
|
||||
|
||||
func (t *Trader) Sell(units float64) {
|
||||
t.closeOrdersAndPositions()
|
||||
t.Log.Printf("Sell %f units", units)
|
||||
t.Broker.MarketOrder(t.Symbol, -units, 0.0, 0.0)
|
||||
t.Broker.Order(Market, t.Symbol, -units, 0, 0, 0)
|
||||
t.stats.tradesThisCandle = append(t.stats.tradesThisCandle, TradeStat{-units, false})
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user