blankly-finance / blankly

🚀 💸 Easily build, backtest and deploy your algo in just a few lines of code. Trade stocks, cryptos, and forex across exchanges w/ one package.
https://package.blankly.finance
GNU Lesser General Public License v3.0
2.03k stars 261 forks source link

Inconsistencies in asset visualisation #244

Open hom-bahrani opened 10 months ago

hom-bahrani commented 10 months ago

Description

I am running the basic RSI example from your documentation and observed some strange behaviour

def price_event(price, symbol, state: blankly.StrategyState):
    """This function will give an updated price every 15 seconds from our definition below"""
    state.variables["history"].append(price)
    rsi = blankly.indicators.rsi(state.variables["history"])
    if rsi[-1] < 30 and not state.variables["owns_position"]:
        # Dollar cost average buy
        buy = int(state.interface.cash / price)
        state.interface.market_order(symbol, side="buy", size=buy)
        state.variables["owns_position"] = True
    elif rsi[-1] > 70 and state.variables["owns_position"]:
        # Dollar cost average sell
        curr_value = state.interface.account[state.base_asset].available
        state.interface.market_order(symbol, side="sell", size=int(curr_value))
        state.variables["owns_position"] = False

def init(symbol, state: blankly.StrategyState):
    # Download price data to give context to the algo
    state.variables["history"] = state.interface.history(
        symbol, to=150, return_as="deque", resolution=state.resolution
    )["close"]
    state.variables["owns_position"] = False

if __name__ == "__main__":
    exchange = blankly.Oanda()

    # Use our strategy helper on coinbase pro
    strategy = blankly.Strategy(exchange)

    # Run the price event function every time we check for a new price - by default that is 15 seconds
    # strategy.add_price_event(price_event, symbol="XAU-USD", resolution="15m", init=init)
    strategy.add_price_event(price_event, symbol="BCO-USD", resolution="15m", init=init)

    # Start the strategy. This will begin each of the price event ticks
    # strategy.start()
    # Or backtest using this
    results = strategy.backtest(to="1y")
    print(results)

I get a nice html plot at the end of backtesting, I noticed that the code above is using all the cash in my account [state.interface.cash](http://state.interface.cash) and that number is being used to determine buy size. But say I want a stake of e.g. $1000 rather than use all the cash in my account, when I change the code to calculate the buy size using the stake it has a bizarre effect on the BCO (crude oil) plot - the y axis is completely different? (I’ve attached the plots)

stake = 1000  # 1000 USD
buy = int(stake / price)
state.interface.market_order(symbol, side="buy", size=buy)

Any idea why this might be case? everything else is the same in both cases so I would expect the BCO plot to remain the same. The same thing happens if I manually set the size e.g. to 1 state.interface.market_order(symbol, side="buy", size=1) In this case I see the BCO plot bars are all set to 1?

Screenshot 2023-09-07 at 16 16 49 Screenshot 2023-09-07 at 16 16 21 Screenshot 2023-09-07 at 16 16 14
EmersonDove commented 9 months ago

This seems like correct behavior, I dont't know the value of your account of course so I can't really explain why it's only trading one share, maybe by default you have slightly more cash than 1 share and its getting trunced to an int? I would have to see the numbers to get a better idea.