Last active
July 17, 2022 16:19
-
-
Save ttt733/3d5b47950a65157cf3c78dc7c6aab071 to your computer and use it in GitHub Desktop.
Mean Reversion Trading Algorithm (Alpaca)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Alpaca.Markets; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading; | |
namespace Examples | |
{ | |
class MeanReversionPaperOnly | |
{ | |
private string API_KEY = "REPLACEME"; | |
private string API_SECRET = "REPLACEME"; | |
private string API_URL = "https://paper-api.alpaca.markets"; | |
private string symbol = "SPY"; | |
private Decimal scale = 200; | |
private RestClient restClient; | |
private Guid lastTradeId = Guid.NewGuid(); | |
private List<Decimal> closingPrices = new List<Decimal>(); | |
public void Run() | |
{ | |
restClient = new RestClient(API_KEY, API_SECRET, API_URL); | |
// First, cancel any existing orders so they don't impact our buying power. | |
var orders = restClient.ListOrdersAsync().Result; | |
foreach (var order in orders) | |
{ | |
restClient.DeleteOrderAsync(order.OrderId); | |
} | |
// Figure out when the market will close so we can prepare to sell beforehand. | |
var calendars = restClient.ListCalendarAsync(DateTime.Today).Result; | |
var calendarDate = calendars.First().TradingDate; | |
var closingTime = calendars.First().TradingCloseTime; | |
closingTime = new DateTime(calendarDate.Year, calendarDate.Month, calendarDate.Day, closingTime.Hour, closingTime.Minute, closingTime.Second); | |
Console.WriteLine("Waiting for market open..."); | |
AwaitMarketOpen(); | |
Console.WriteLine("Market opened."); | |
// Check every minute for price updates. | |
TimeSpan timeUntilClose = closingTime - DateTime.UtcNow; | |
while (timeUntilClose.TotalMinutes > 15) | |
{ | |
// Cancel old order if it's not already been filled. | |
restClient.DeleteOrderAsync(lastTradeId); | |
// Get information about current account value. | |
var account = restClient.GetAccountAsync().Result; | |
Decimal buyingPower = account.BuyingPower; | |
Decimal portfolioValue = account.PortfolioValue; | |
// Get information about our existing position. | |
int positionQuantity = 0; | |
Decimal positionValue = 0; | |
try | |
{ | |
var currentPosition = restClient.GetPositionAsync(symbol).Result; | |
positionQuantity = currentPosition.Quantity; | |
positionValue = currentPosition.MarketValue; | |
} | |
catch (Exception e) | |
{ | |
// No position exists. This exception can be safely ignored. | |
} | |
var barSet = restClient.GetBarSetAsync(new String[] { symbol }, TimeFrame.Minute, 20).Result; | |
var bars = barSet[symbol]; | |
Decimal avg = bars.Average(item => item.Close); | |
Decimal currentPrice = bars.Last().Close; | |
Decimal diff = avg - currentPrice; | |
if (diff <= 0) | |
{ | |
// Above the 20 minute average - exit any existing long position. | |
if (positionQuantity > 0) | |
{ | |
Console.WriteLine("Setting position to zero."); | |
SubmitOrder(positionQuantity, currentPrice, OrderSide.Sell); | |
} | |
else | |
{ | |
Console.WriteLine("No position to exit."); | |
} | |
} | |
else | |
{ | |
// Allocate a percent of our portfolio to this position. | |
Decimal portfolioShare = diff / currentPrice * scale; | |
Decimal targetPositionValue = portfolioValue * portfolioShare; | |
Decimal amountToAdd = targetPositionValue - positionValue; | |
if (amountToAdd > 0) | |
{ | |
// Buy as many shares as we can without going over amountToAdd. | |
// Make sure we're not trying to buy more than we can. | |
if (amountToAdd > buyingPower) | |
{ | |
amountToAdd = buyingPower; | |
} | |
int qtyToBuy = (int)(amountToAdd / currentPrice); | |
SubmitOrder(qtyToBuy, currentPrice, OrderSide.Buy); | |
} | |
else | |
{ | |
// Sell as many shares as we can without going under amountToAdd. | |
// Make sure we're not trying to sell more than we have. | |
amountToAdd *= -1; | |
int qtyToSell = (int)(amountToAdd / currentPrice); | |
if (qtyToSell > positionQuantity) | |
{ | |
qtyToSell = positionQuantity; | |
} | |
SubmitOrder(qtyToSell, currentPrice, OrderSide.Sell); | |
} | |
} | |
// Wait another minute. | |
Thread.Sleep(60000); | |
timeUntilClose = closingTime - DateTime.Now; | |
} | |
Console.WriteLine("Market nearing close; closing position."); | |
ClosePositionAtMarket(); | |
} | |
private void AwaitMarketOpen() | |
{ | |
while (!restClient.GetClockAsync().Result.IsOpen) | |
{ | |
Thread.Sleep(60000); | |
} | |
} | |
// Submit an order if quantity is not zero. | |
private void SubmitOrder(int quantity, Decimal price, OrderSide side) | |
{ | |
if (quantity == 0) | |
{ | |
Console.WriteLine("No order necessary."); | |
return; | |
} | |
Console.WriteLine($"Submitting {side} order for {quantity} shares at ${price}."); | |
var order = restClient.PostOrderAsync(symbol, quantity, side, OrderType.Limit, TimeInForce.Day, price).Result; | |
lastTradeId = order.OrderId; | |
} | |
private void ClosePositionAtMarket() | |
{ | |
try | |
{ | |
var positionQuantity = restClient.GetPositionAsync(symbol).Result.Quantity; | |
restClient.PostOrderAsync(symbol, positionQuantity, OrderSide.Sell, OrderType.Market, TimeInForce.Day); | |
} | |
catch (Exception e) | |
{ | |
// No position to exit. | |
} | |
} | |
} | |
} |
// F# version - compiles, not tested
// alpaca.markets 3.0.3 seemed to compile
open System.Threading.Tasks
let API_KEY = "REPLACEME"
let API_SECRET = "REPLACEME"
let API_URL = "https://paper-api.alpaca.markets"
let symbol = "SPY"
let scale = 200m
let mutable restClient: RestClient = null
let mutable lastTradeId = Guid.NewGuid()
let closingPrices = ResizeArray<Decimal>();
let awaitMarketOpen() =
while not (restClient.GetClockAsync().Result.IsOpen) do
Thread.Sleep(60000)
// Submit an order if quantity is not zero.
let submitOrder(quantity:int, price:Decimal, side:OrderSide ) =
if quantity = 0 then
Console.WriteLine("No order necessary.");
else
printfn "Submitting %A order for %A shares at $%A." side quantity price
let order = restClient.PostOrderAsync(symbol, int64 quantity, side, OrderType.Limit, TimeInForce.Day, Nullable price).Result;
lastTradeId <- order.OrderId;
let closePositionAtMarket() =
try
let positionQuantity = restClient.GetPositionAsync(symbol).Result.Quantity
restClient.PostOrderAsync(symbol, int64 positionQuantity, OrderSide.Sell, OrderType.Market, TimeInForce.Day)
|> ignore<Task<_>>
with e -> () // No position to exit.
let run () =
restClient <- new RestClient(API_KEY, API_SECRET, API_URL);
// First, cancel any existing orders so they don't impact our buying power.
let orders = restClient.ListOrdersAsync().Result;
orders
|> Seq.iter(fun order ->
restClient.DeleteOrderAsync(order.OrderId)
|> ignore<Task<_>>
)
// Figure out when the market will close so we can prepare to sell beforehand.
let calendars = restClient.ListCalendarAsync(Nullable DateTime.Today).Result
let calendarDate = calendars.First().TradingDate
let mutable closingTime = calendars.First().TradingCloseTime
closingTime <- new DateTime(calendarDate.Year, calendarDate.Month, calendarDate.Day, closingTime.Hour, closingTime.Minute, closingTime.Second)
printfn "Waiting for market open..."
awaitMarketOpen()
printfn "Market opened."
// Check every minute for price updates.
let mutable timeUntilClose = closingTime - DateTime.UtcNow;
while timeUntilClose.TotalMinutes > 15. do
// Cancel old order if it's not already been filled.
restClient.DeleteOrderAsync(lastTradeId)
|> ignore<Task<bool>>
// Get information about current account value.
let account = restClient.GetAccountAsync().Result
let buyingPower = account.BuyingPower
let portfolioValue = account.PortfolioValue
// Get information about our existing position.
let mutable positionQuantity = 0
let mutable positionValue = 0m
try
let currentPosition = restClient.GetPositionAsync(symbol).Result
positionQuantity <- currentPosition.Quantity
positionValue <- currentPosition.MarketValue
with e ->
// No position exists. This exception can be safely ignored.
()
let barSet = restClient.GetBarSetAsync(Array.singleton symbol, TimeFrame.Minute, Nullable 20).Result
let bars = barSet.[symbol]
let avg = bars.Average(fun item -> item.Close)
let currentPrice = bars.Last().Close
let diff = avg - currentPrice
if diff <= 0m then
// Above the 20 minute average - exit any existing long position.
if (positionQuantity > 0) then
Console.WriteLine("Setting position to zero.");
submitOrder(int positionQuantity, currentPrice, OrderSide.Sell);
else
Console.WriteLine("No position to exit.");
else
// Allocate a percent of our portfolio to this position.
let portfolioShare = diff / currentPrice * scale;
let targetPositionValue = portfolioValue * portfolioShare;
let mutable amountToAdd = targetPositionValue - positionValue;
if (amountToAdd > 0m) then
// Buy as many shares as we can without going over amountToAdd.
// Make sure we're not trying to buy more than we can.
if amountToAdd > buyingPower then
amountToAdd <- buyingPower
let qtyToBuy = int (amountToAdd / currentPrice)
submitOrder(int qtyToBuy, currentPrice, OrderSide.Buy)
else
// Sell as many shares as we can without going under amountToAdd.
// Make sure we're not trying to sell more than we have.
amountToAdd <- amountToAdd * -1m
let mutable qtyToSell = int (amountToAdd / currentPrice)
if qtyToSell > positionQuantity then
qtyToSell <- positionQuantity
submitOrder(int qtyToSell, currentPrice, OrderSide.Sell)
// Wait another minute.
Thread.Sleep(60000)
timeUntilClose <- closingTime - DateTime.Now;
Console.WriteLine("Market nearing close; closing position.");
closePositionAtMarket()
type
RestClient
isn't found. what package or namespace should it be in?
I'm having the same issue.
type
RestClient
isn't found. what package or namespace should it be in?I'm having the same issue.
It was an older version try alpaca.markets 3.0.3
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
type
RestClient
isn't found. what package or namespace should it be in?