mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-06-15 00:13:51 +00:00
Implemented TP, SL, TSL
This commit is contained in:
parent
5a0a4d0c33
commit
56740b5a00
159
backtesting.go
159
backtesting.go
@ -20,7 +20,8 @@ import (
|
||||
var (
|
||||
ErrEOF = errors.New("end of the input data")
|
||||
ErrNoData = errors.New("no data")
|
||||
ErrPositionClosed = errors.New("position closed")
|
||||
ErrPositionClosed = errors.New("position already closed")
|
||||
ErrZeroUnits = errors.New("no amount of units specifed")
|
||||
)
|
||||
|
||||
var _ Broker = (*TestBroker)(nil) // Compile-time interface check.
|
||||
@ -56,9 +57,11 @@ func Backtest(trader *Trader) {
|
||||
{
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintf(w, "Timespan:\t%s\t\n", stats.Dated.Date(-1).Sub(stats.Dated.Date(0)).Round(time.Second))
|
||||
fmt.Fprintf(w, "Net Profit:\t$%.2f (%.2f%%)\t\n", profit, 100*profit/stats.Dated.Float("Equity", 0))
|
||||
fmt.Fprintf(w, "Profit Factor:\t%.2f\t\n", profitFactor)
|
||||
fmt.Fprintf(w, "Max Drawdown:\t$%.2f (%.2f%%)\t\n", maxDrawdown, maxDrawdownPct)
|
||||
fmt.Fprintf(w, "Spread collected:\t$%.2f\t\n", broker.spreadCollectedUSD)
|
||||
fmt.Fprintln(w)
|
||||
w.Flush()
|
||||
}
|
||||
@ -328,9 +331,10 @@ type TestBroker struct {
|
||||
Spread float64 // Number of pips to add to the price when buying and subtract when selling. (Forex)
|
||||
Slippage float64 // A percentage of the price to add when buying and subtract when selling.
|
||||
|
||||
candleCount int // The number of candles anyone outside this broker has seen. Also equal to the number of times Candles has been called.
|
||||
orders []Order
|
||||
positions []Position
|
||||
candleCount int // The number of candles anyone outside this broker has seen. Also equal to the number of times Candles has been called.
|
||||
orders []Order
|
||||
positions []Position
|
||||
spreadCollectedUSD float64 // Total amount of spread collected from trades.
|
||||
}
|
||||
|
||||
func NewTestBroker(dataBroker Broker, data *DataFrame, cash, leverage, spread float64, startCandles int) *TestBroker {
|
||||
@ -340,25 +344,70 @@ func NewTestBroker(dataBroker Broker, data *DataFrame, cash, leverage, spread fl
|
||||
Cash: cash,
|
||||
Leverage: Max(leverage, 1),
|
||||
Spread: spread,
|
||||
Slippage: 0.005, // Price +/- 0.5%
|
||||
Slippage: 0.005, // Price +/- up to 0.5% by a random amount.
|
||||
candleCount: Max(startCandles, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// SpreadCollected returns the total amount of spread collected from trades, in USD.
|
||||
func (b *TestBroker) SpreadCollected() float64 {
|
||||
return b.spreadCollectedUSD
|
||||
}
|
||||
|
||||
// CandleIndex returns the index of the current candle.
|
||||
func (b *TestBroker) CandleIndex() int {
|
||||
return Max(b.candleCount-1, 0)
|
||||
}
|
||||
|
||||
// Advance advances the test broker to the next candle in the input data. This should be done at the end of the
|
||||
// strategy loop.
|
||||
// strategy loop. This will also call Tick() to update orders and positions.
|
||||
func (b *TestBroker) Advance() {
|
||||
if b.candleCount < b.Data.Len() {
|
||||
b.candleCount++
|
||||
}
|
||||
b.Tick()
|
||||
}
|
||||
|
||||
// Bid returns the price a seller pays for the current candle.
|
||||
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())
|
||||
for _, any_p := range b.positions {
|
||||
if any_p.Closed() {
|
||||
continue
|
||||
}
|
||||
p := any_p.(*TestPosition)
|
||||
price := b.Price("", p.units < 0) // We want to buy if we are short, and vice versa.
|
||||
|
||||
if p.trailingSLDist > 0 {
|
||||
p.trailingSL = Max(p.trailingSL, price-p.trailingSLDist)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
} else if p.stopLoss > 0 {
|
||||
if (p.units > 0 && p.stopLoss >= low) || (p.units < 0 && p.stopLoss <= high) {
|
||||
p.close(p.stopLoss, closeTypeStopLoss)
|
||||
}
|
||||
} else if p.trailingSL > 0 {
|
||||
if (p.units > 0 && p.trailingSL >= low) || (p.units < 0 && p.trailingSL <= high) {
|
||||
p.close(p.trailingSL, closeTypeTrailingStop)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Price returns the ask price if wantToBuy is true and the bid price if wantToBuy is false.
|
||||
func (b *TestBroker) Price(symbol string, wantToBuy bool) float64 {
|
||||
if wantToBuy {
|
||||
return b.Ask(symbol)
|
||||
}
|
||||
return b.Bid(symbol)
|
||||
}
|
||||
|
||||
// Bid returns the price a seller receives for the current candle.
|
||||
func (b *TestBroker) Bid(_ string) float64 {
|
||||
return b.Data.Close(b.CandleIndex())
|
||||
}
|
||||
@ -390,6 +439,9 @@ func (b *TestBroker) Candles(symbol string, frequency string, count int) (*DataF
|
||||
}
|
||||
|
||||
func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takeProfit float64) (Order, error) {
|
||||
if units == 0 {
|
||||
return nil, ErrZeroUnits
|
||||
}
|
||||
if b.Data == nil { // The DataBroker could have data but nobody has fetched it, yet.
|
||||
if b.DataBroker == nil {
|
||||
return nil, ErrNoData
|
||||
@ -400,28 +452,32 @@ func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takePro
|
||||
}
|
||||
}
|
||||
|
||||
var price float64
|
||||
if units < 0 {
|
||||
price = b.Bid("")
|
||||
} else {
|
||||
price = b.Ask("")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
order := &TestOrder{
|
||||
id: strconv.Itoa(rand.Int()),
|
||||
leverage: b.Leverage,
|
||||
position: nil,
|
||||
price: price,
|
||||
symbol: symbol,
|
||||
stopLoss: stopLoss,
|
||||
takeProfit: takeProfit,
|
||||
time: time.Now(),
|
||||
orderType: MarketOrder,
|
||||
units: units,
|
||||
}
|
||||
if trailingSL > 0 {
|
||||
order.trailingSL = trailingSL
|
||||
} else {
|
||||
order.stopLoss = stopLoss
|
||||
}
|
||||
|
||||
// Instantly fulfill the order.
|
||||
order.position = &TestPosition{
|
||||
@ -431,11 +487,15 @@ func (b *TestBroker) MarketOrder(symbol string, units float64, stopLoss, takePro
|
||||
id: strconv.Itoa(rand.Int()),
|
||||
leverage: b.Leverage,
|
||||
symbol: symbol,
|
||||
stopLoss: stopLoss,
|
||||
takeProfit: takeProfit,
|
||||
time: time.Now(),
|
||||
units: units,
|
||||
}
|
||||
if trailingSL > 0 {
|
||||
order.position.trailingSLDist = trailingSL
|
||||
} else {
|
||||
order.position.stopLoss = stopLoss
|
||||
}
|
||||
b.Cash -= order.position.EntryValue()
|
||||
|
||||
b.orders = append(b.orders, order)
|
||||
@ -493,38 +553,54 @@ func (b *TestBroker) Positions() []Position {
|
||||
}
|
||||
|
||||
type TestPosition struct {
|
||||
broker *TestBroker
|
||||
closed bool
|
||||
entryPrice float64
|
||||
closePrice float64 // If zero, then position has not been closed.
|
||||
id string
|
||||
leverage float64
|
||||
symbol string
|
||||
stopLoss float64
|
||||
takeProfit float64
|
||||
time time.Time
|
||||
units float64
|
||||
broker *TestBroker
|
||||
closed bool
|
||||
entryPrice float64
|
||||
closePrice float64 // If zero, then position has not been closed.
|
||||
closeType string // SL, TS, TP
|
||||
id string
|
||||
leverage float64
|
||||
symbol string
|
||||
trailingSL float64 // the price of the trailing stop loss as assigned by broker Tick().
|
||||
trailingSLDist float64 // serves to calculate the trailing stop loss at the broker.
|
||||
stopLoss float64
|
||||
takeProfit float64
|
||||
time time.Time
|
||||
units float64
|
||||
}
|
||||
|
||||
func (p *TestPosition) Close() error {
|
||||
p.close(p.broker.Price("", p.units < 0), closeTypeMarket)
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
closeTypeMarket = "M"
|
||||
closeTypeStopLoss = "SL"
|
||||
closeTypeTrailingStop = "TS"
|
||||
closeTypeTakeProfit = "TP"
|
||||
)
|
||||
|
||||
func (p *TestPosition) close(atPrice float64, closeType string) {
|
||||
if p.closed {
|
||||
return ErrPositionClosed
|
||||
return
|
||||
}
|
||||
p.closed = true
|
||||
if p.units < 0 {
|
||||
p.closePrice = p.broker.Ask("") // Ask because we are short so we have to buy.
|
||||
} else {
|
||||
p.closePrice = p.broker.Bid("") // Ask because we are long so we have to sell.
|
||||
}
|
||||
p.closePrice = atPrice
|
||||
p.closeType = closeType
|
||||
p.broker.Cash += p.Value() // Return the value of the position to the broker.
|
||||
p.broker.spreadCollectedUSD += p.broker.Spread * p.units
|
||||
p.broker.SignalEmit("PositionClosed", p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TestPosition) Closed() bool {
|
||||
return p.closed
|
||||
}
|
||||
|
||||
func (p *TestPosition) CloseType() string {
|
||||
return p.closeType
|
||||
}
|
||||
|
||||
func (p *TestPosition) EntryPrice() float64 {
|
||||
return p.entryPrice
|
||||
}
|
||||
@ -553,6 +629,10 @@ func (p *TestPosition) Symbol() string {
|
||||
return p.symbol
|
||||
}
|
||||
|
||||
func (p *TestPosition) TrailingStop() float64 {
|
||||
return p.trailingSL
|
||||
}
|
||||
|
||||
func (p *TestPosition) StopLoss() float64 {
|
||||
return p.stopLoss
|
||||
}
|
||||
@ -573,13 +653,7 @@ func (p *TestPosition) Value() float64 {
|
||||
if p.closed {
|
||||
return p.closePrice * p.units
|
||||
}
|
||||
var price float64
|
||||
if p.units < 0 {
|
||||
price = p.broker.Ask("")
|
||||
} else {
|
||||
price = p.broker.Bid("")
|
||||
}
|
||||
return price * p.units
|
||||
return p.broker.Price("", p.units > 0) * p.units
|
||||
}
|
||||
|
||||
type TestOrder struct {
|
||||
@ -588,6 +662,7 @@ type TestOrder struct {
|
||||
position *TestPosition
|
||||
price float64
|
||||
symbol string
|
||||
trailingSL float64
|
||||
stopLoss float64
|
||||
takeProfit float64
|
||||
time time.Time
|
||||
@ -623,6 +698,10 @@ func (o *TestOrder) Symbol() string {
|
||||
return o.symbol
|
||||
}
|
||||
|
||||
func (o *TestOrder) TrailingStop() float64 {
|
||||
return o.trailingSL
|
||||
}
|
||||
|
||||
func (o *TestOrder) StopLoss() float64 {
|
||||
return o.stopLoss
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ const testDataCSV = `date,open,high,low,close,volume
|
||||
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
|
||||
2022-01-08,1.25,1.3,1.2,1.1,150
|
||||
2022-01-08,1.25,1.3,1.0,1.1,150
|
||||
2022-01-09,1.1,1.4,1.0,1.3,220`
|
||||
|
||||
func newTestingDataframe() *DataFrame {
|
||||
@ -180,3 +180,98 @@ func TestBacktestingBrokerOrders(t *testing.T) {
|
||||
t.Errorf("Expected broker PL to be 2500, got %f", broker.PL())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktestingBrokerStopLimitOrders(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)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if order == nil {
|
||||
t.Fatal("Order is nil")
|
||||
}
|
||||
if order.StopLoss() != 1.05 {
|
||||
t.Errorf("Expected stop loss to be 1.1, got %f", order.StopLoss())
|
||||
}
|
||||
if order.TakeProfit() != 1.25 {
|
||||
t.Errorf("Expected take profit to be 1.25, got %f", order.TakeProfit())
|
||||
}
|
||||
|
||||
position := order.Position()
|
||||
if position == nil {
|
||||
t.Fatal("Position is nil")
|
||||
}
|
||||
if position.StopLoss() != 1.05 {
|
||||
t.Errorf("Expected stop loss to be 1.1, got %f", position.StopLoss())
|
||||
}
|
||||
if position.TakeProfit() != 1.25 {
|
||||
t.Errorf("Expected take profit to be 1.25, got %f", position.TakeProfit())
|
||||
}
|
||||
|
||||
broker.Advance()
|
||||
broker.Advance() // Now we're at the third candle which hits our take profit
|
||||
|
||||
if position.Closed() != true {
|
||||
t.Error("Expected position to be closed")
|
||||
}
|
||||
if position.ClosePrice() != 1.25 {
|
||||
t.Errorf("Expected close price to be 1.25, got %f", position.ClosePrice())
|
||||
}
|
||||
if !EqualApprox(position.PL(), 1000) { // (1.25-1.15) * 10_000 = 1000
|
||||
t.Errorf("Expected position PL to be 1000, got %f", position.PL())
|
||||
}
|
||||
|
||||
broker.Advance() // 4th candle
|
||||
|
||||
order, err = broker.MarketOrder("", 10_000, -0.2, 1.4) // Long position with trailing stop loss of 0.2.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if order == nil {
|
||||
t.Fatal("Order is nil")
|
||||
}
|
||||
if order.StopLoss() != 0 {
|
||||
t.Errorf("Expected stop loss to be 0, got %f", order.StopLoss())
|
||||
}
|
||||
if order.TakeProfit() != 1.4 {
|
||||
t.Errorf("Expected take profit to be 1.4, got %f", order.TakeProfit())
|
||||
}
|
||||
if !EqualApprox(order.TrailingStop(), 0.2) { // Orders return the distance to the trailing stop loss.
|
||||
t.Errorf("Expected trailing stop to be 0.2, got %f", order.TrailingStop())
|
||||
}
|
||||
|
||||
broker.Advance() // Cause the position to get updated.
|
||||
position = order.Position()
|
||||
if position == nil {
|
||||
t.Fatal("Position is nil")
|
||||
}
|
||||
if position.Closed() {
|
||||
t.Error("Expected position to be open")
|
||||
}
|
||||
if position.StopLoss() != 0 {
|
||||
t.Errorf("Expected stop loss to be 0, got %f", position.StopLoss())
|
||||
}
|
||||
if position.TakeProfit() != 1.4 {
|
||||
t.Errorf("Expected take profit to be 1.4, got %f", position.TakeProfit())
|
||||
}
|
||||
if !EqualApprox(position.TrailingStop(), 0.95) { // Positions return the actual trailing stop loss price.
|
||||
t.Errorf("Expected trailing stop to be 0.95, got %f", position.TrailingStop())
|
||||
}
|
||||
|
||||
for !position.Closed() {
|
||||
broker.Advance() // Advance until position is closed.
|
||||
}
|
||||
|
||||
if !EqualApprox(position.ClosePrice(), 1.05) {
|
||||
t.Errorf("Expected close price to be 1.05, got %f", position.ClosePrice())
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
61
broker.go
61
broker.go
@ -21,35 +21,38 @@ var (
|
||||
)
|
||||
|
||||
type Order interface {
|
||||
Cancel() error // Cancel attempts to cancel the order and returns an error if it fails. If the error is nil, the order was canceled.
|
||||
Fulfilled() bool // Fulfilled returns true if the order has been filled with the broker and a position is active.
|
||||
Id() string // Id returns the unique identifier of the order by the broker.
|
||||
Leverage() float64 // Leverage returns the leverage of the order.
|
||||
Position() Position // Position returns the position of the order. If the order has not been filled, nil is returned.
|
||||
Price() float64 // Price returns the price of the symbol at the time the order was placed.
|
||||
Symbol() string // Symbol returns the symbol name of the order.
|
||||
StopLoss() float64 // StopLoss returns the stop loss price of the order.
|
||||
TakeProfit() float64 // TakeProfit returns the take profit price of the order.
|
||||
Time() time.Time // Time returns the time the order was placed.
|
||||
Type() OrderType // Type returns the type of order.
|
||||
Units() float64 // Units returns the number of units purchased or sold by the order.
|
||||
Cancel() error // Cancel attempts to cancel the order and returns an error if it fails. If the error is nil, the order was canceled.
|
||||
Fulfilled() bool // Fulfilled returns true if the order has been filled with the broker and a position is active.
|
||||
Id() string // Id returns the unique identifier of the order by the broker.
|
||||
Leverage() float64 // Leverage returns the leverage of the order.
|
||||
Position() Position // Position returns the position of the order. If the order has not been filled, nil is returned.
|
||||
Price() float64 // Price returns the price of the symbol at the time the order was placed.
|
||||
Symbol() string // Symbol returns the symbol name of the order.
|
||||
TrailingStop() float64 // TrailingStop returns the trailing stop loss distance of the order.
|
||||
StopLoss() float64 // StopLoss returns the stop loss price of the order.
|
||||
TakeProfit() float64 // TakeProfit returns the take profit price of the order.
|
||||
Time() time.Time // Time returns the time the order was placed.
|
||||
Type() OrderType // Type returns the type of order.
|
||||
Units() float64 // Units returns the number of units purchased or sold by the order.
|
||||
}
|
||||
|
||||
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.
|
||||
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.
|
||||
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() 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.
|
||||
}
|
||||
|
||||
// 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:
|
||||
@ -57,10 +60,12 @@ type Position interface {
|
||||
// - PositionClosed(Position) - Emitted after a position is closed either manually or automatically.
|
||||
type Broker interface {
|
||||
Signaler
|
||||
Bid(symbol string) float64 // Bid returns the sell price of the symbol.
|
||||
Ask(symbol string) float64 // Ask returns the buy price of the symbol, which is typically higher than the sell price.
|
||||
Price(symbol string, wantToBuy bool) float64 // Price returns the ask price if wantToBuy is true and the bid price if wantToBuy is false.
|
||||
Bid(symbol string) float64 // Bid returns the sell price of the symbol.
|
||||
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)
|
||||
NAV() float64 // NAV returns the net asset value of the account.
|
||||
PL() float64 // PL returns the profit or loss of the account.
|
||||
|
@ -42,6 +42,14 @@ func NewOandaBroker(token, accountID string, practice bool) *OandaBroker {
|
||||
}
|
||||
}
|
||||
|
||||
// Price returns the ask price if wantToBuy is true and the bid price if wantToBuy is false.
|
||||
func (b *OandaBroker) Price(symbol string, wantToBuy bool) float64 {
|
||||
if wantToBuy {
|
||||
return b.Ask(symbol)
|
||||
}
|
||||
return b.Bid(symbol)
|
||||
}
|
||||
|
||||
func (b *OandaBroker) Bid(symbol string) float64 {
|
||||
return 0
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user