Skip to content

Instantly share code, notes, and snippets.

@ttt733
Last active July 17, 2022 16:19
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ttt733/3d5b47950a65157cf3c78dc7c6aab071 to your computer and use it in GitHub Desktop.
Save ttt733/3d5b47950a65157cf3c78dc7c6aab071 to your computer and use it in GitHub Desktop.
Mean Reversion Trading Algorithm (Alpaca)
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.
}
}
}
}
@Gabowatt
Copy link

type RestClient isn't found. what package or namespace should it be in?

I'm having the same issue.

@ImaginaryDevelopment
Copy link

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