The close of positions near the end of day doesn't always give them enough time to complete and shorting happens as a result here. This should prevent the unintended short shares and is a best practice for closing a position ...
Replace these lines:
order(stock, -1 * context.amount[stock])
...with:
order_target(stock, 0)
And schedule_function is great, it can replace the exchange_time.hour etc.
Another thing is that the code is investing quite a bit more than the $1000 initial capital. Try 7597 in that field to be spending 100% and then you can use the returns value and compare to your calculations.
Also record(pnl = context.portfolio.pnl) can help there.
We place an order one minute and it is filled at its next minute price. Rather than that code, using context.portfolio.positions[stock].cost_basis is better, it is the per share cost based on the price at the time of the trade. Its counterpart context.portfolio.positions[stock].amount is the number of shares. When the sell is placed, the price isn't the same as the actual sell, and there are incomplete fills in this code. If there are partial fills, you can see it becomes complicated quickly with shares selling at different prices. With those changes, my own code shows AMD taking a loss, -784, the others pretty flat. Hope those things will be useful.
To answer your question, when it comes to trading on Robinhood via Quantopian, all free, amazing isn't it. Optional third party data sources do require a subscription with those other companies.