Refactor market into two files

This commit is contained in:
Mark Veidemanis 2022-12-20 07:20:26 +00:00
parent b7c46ba1d3
commit a41a1e76a5
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
5 changed files with 164 additions and 159 deletions

View File

@ -1,6 +1,6 @@
from django.test import TestCase from django.test import TestCase
from core.lib.market import check_conflicting_position, check_existing_position from core.trading.crossfilter import check_conflicting_position, check_existing_position
class MarketTestCase(TestCase): class MarketTestCase(TestCase):

0
core/trading/__init__.py Normal file
View File

161
core/trading/crossfilter.py Normal file
View File

@ -0,0 +1,161 @@
from core.exchanges import GenericAPIError
from core.util import logs
log = logs.get_logger(__name__)
def check_existing_position(
func: str,
position: dict,
open_side: str,
open_symbol: str,
open_units: str,
new_side: str,
new_symbol: str,
trade_side_opposite: str,
):
# Check if we already have a position for the symbol
if open_symbol == new_symbol:
# If the live side is the inverse of what we want to do,
# we can't open a position
if open_side == trade_side_opposite:
# If there is a position open, we can't open a new one in the opposite
# direction
if open_units != "0":
# If we have a short on GBP/AUD, we can only place more shorts on
# GBP/AUD.
if func == "entry":
log.debug(
f"Refusing to open new {new_side} position on {new_symbol} due "
f"to {open_side} position on {open_symbol}"
)
return {
"action": "rejected",
"positions": position,
}
elif func == "exit":
log.debug(
(
f"Found {open_units} units of "
f"{open_symbol} on side {trade_side_opposite}"
)
)
# Pass back opposing side so we can close it
return {
"action": "close",
"side": trade_side_opposite,
"positions": position,
}
return False
def check_conflicting_position(
func: str,
position: dict,
open_base: str,
open_quote: str,
open_side: str,
open_symbol: str,
open_units: str,
new_base: str,
new_quote: str,
new_side: str,
new_symbol: str,
trade_side_opposite: str,
):
if open_base == new_quote or open_quote == new_base:
# If we have a long on GBP/AUD, we can only place shorts on XAU/GBP.
if open_side != trade_side_opposite:
if open_units != "0":
# Only do this for entries
if func == "entry":
log.debug(
f"Refusing to open {new_side} position on {new_symbol} due to "
f"{open_side} position on {open_symbol}"
)
return {
"action": "rejected",
"positions": position,
}
def crossfilter(account, new_symbol, new_direction, func):
"""
Determine if we are betting against ourselves.
Checks open positions for the account, rejecting the trade if there is one
with an opposite direction to this one.
:param account: Account object
:param symbol: Symbol
:param direction: Direction of the trade
:param func: Whether we are checking entries or exits
:return: dict of action and opposing position, or False
"""
try:
# Only get the data we need
if func == "entry":
all_positions = account.client.get_all_positions()
else:
all_positions = [account.client.get_position_info(new_symbol)]
except GenericAPIError as e:
if "No position exists for the specified instrument" in str(e):
log.debug("No position exists for this symbol")
return False
else:
log.error(f"Error getting position info: {e}")
return None
if new_direction == "buy":
opposing_side = "short"
new_side = "long"
elif new_direction == "sell":
opposing_side = "long"
new_side = "short"
quotes = []
new_base, new_quote = new_symbol.split("_")
for position in all_positions:
# For Forex, get a list of all the quotes.
# This is to prevent betting against ourselves.
# Consider we have a long position open, EUR/USD, and we want to open a
# long position on USD/JPY. If the first goes up, the second one will go
# down just as much. We won't make any money.
if "_" in position["symbol"]:
open_base, open_quote = position["symbol"].split("_")
quotes.append(open_quote)
open_symbol = position["symbol"]
open_side = position["side"]
open_base, open_quote = open_symbol.split("_")
# Check if we already have a position
existing_position_check = check_existing_position(
func=func,
position=position,
open_side=open_side,
open_symbol=open_symbol,
open_units=position["units"],
new_side=new_side,
new_symbol=new_symbol,
trade_side_opposite=opposing_side,
)
if existing_position_check:
return existing_position_check
# Check if we are betting against ourselves
conflicting_position_check = check_conflicting_position(
func=func,
position=position,
open_base=open_base,
open_quote=open_quote,
open_side=open_side,
open_symbol=open_symbol,
open_units=position["units"],
new_base=new_base,
new_quote=new_quote,
new_side=new_side,
new_symbol=new_symbol,
trade_side_opposite=opposing_side,
)
if conflicting_position_check:
return conflicting_position_check
return False

View File

@ -4,168 +4,12 @@ from decimal import Decimal as D
from core.exchanges import GenericAPIError from core.exchanges import GenericAPIError
from core.lib.notify import sendmsg from core.lib.notify import sendmsg
from core.models import Account, Strategy, Trade from core.models import Account, Strategy, Trade
from core.trading.crossfilter import crossfilter
from core.util import logs from core.util import logs
log = logs.get_logger(__name__) log = logs.get_logger(__name__)
def check_existing_position(
func: str,
position: dict,
open_side: str,
open_symbol: str,
open_units: str,
new_side: str,
new_symbol: str,
trade_side_opposite: str,
):
# Check if we already have a position for the symbol
if open_symbol == new_symbol:
# If the live side is the inverse of what we want to do,
# we can't open a position
if open_side == trade_side_opposite:
# If there is a position open, we can't open a new one in the opposite
# direction
if open_units != "0":
# If we have a short on GBP/AUD, we can only place more shorts on
# GBP/AUD.
if func == "entry":
log.debug(
f"Refusing to open new {new_side} position on {new_symbol} due "
f"to {open_side} position on {open_symbol}"
)
return {
"action": "rejected",
"positions": position,
}
elif func == "exit":
log.debug(
(
f"Found {open_units} units of "
f"{open_symbol} on side {trade_side_opposite}"
)
)
# Pass back opposing side so we can close it
return {
"action": "close",
"side": trade_side_opposite,
"positions": position,
}
return False
def check_conflicting_position(
func: str,
position: dict,
open_base: str,
open_quote: str,
open_side: str,
open_symbol: str,
open_units: str,
new_base: str,
new_quote: str,
new_side: str,
new_symbol: str,
trade_side_opposite: str,
):
if open_base == new_quote or open_quote == new_base:
# If we have a long on GBP/AUD, we can only place shorts on XAU/GBP.
if open_side != trade_side_opposite:
if open_units != "0":
# Only do this for entries
if func == "entry":
log.debug(
f"Refusing to open {new_side} position on {new_symbol} due to "
f"{open_side} position on {open_symbol}"
)
return {
"action": "rejected",
"positions": position,
}
def crossfilter(account, new_symbol, new_direction, func):
"""
Determine if we are betting against ourselves.
Checks open positions for the account, rejecting the trade if there is one
with an opposite direction to this one.
:param account: Account object
:param symbol: Symbol
:param direction: Direction of the trade
:param func: Whether we are checking entries or exits
:return: dict of action and opposing position, or False
"""
try:
# Only get the data we need
if func == "entry":
all_positions = account.client.get_all_positions()
else:
all_positions = [account.client.get_position_info(new_symbol)]
except GenericAPIError as e:
if "No position exists for the specified instrument" in str(e):
log.debug("No position exists for this symbol")
return False
else:
log.error(f"Error getting position info: {e}")
return None
if new_direction == "buy":
opposing_side = "short"
new_side = "long"
elif new_direction == "sell":
opposing_side = "long"
new_side = "short"
quotes = []
new_base, new_quote = new_symbol.split("_")
for position in all_positions:
# For Forex, get a list of all the quotes.
# This is to prevent betting against ourselves.
# Consider we have a long position open, EUR/USD, and we want to open a
# long position on USD/JPY. If the first goes up, the second one will go
# down just as much. We won't make any money.
if "_" in position["symbol"]:
open_base, open_quote = position["symbol"].split("_")
quotes.append(open_quote)
open_symbol = position["symbol"]
open_side = position["side"]
open_base, open_quote = open_symbol.split("_")
# Check if we already have a position
existing_position_check = check_existing_position(
func=func,
position=position,
open_side=open_side,
open_symbol=open_symbol,
open_units=position["units"],
new_side=new_side,
new_symbol=new_symbol,
trade_side_opposite=opposing_side,
)
if existing_position_check:
return existing_position_check
# Check if we are betting against ourselves
conflicting_position_check = check_conflicting_position(
func=func,
position=position,
open_base=open_base,
open_quote=open_quote,
open_side=open_side,
open_symbol=open_symbol,
open_units=position["units"],
new_base=new_base,
new_quote=new_quote,
new_side=new_side,
new_symbol=new_symbol,
trade_side_opposite=opposing_side,
)
if conflicting_position_check:
return conflicting_position_check
return False
def get_pair(account, base, quote, invert=False): def get_pair(account, base, quote, invert=False):
""" """
Get the pair for the given account and currencies. Get the pair for the given account and currencies.

View File

@ -9,9 +9,9 @@ from rest_framework.parsers import JSONParser
from rest_framework.views import APIView from rest_framework.views import APIView
from core.forms import HookForm from core.forms import HookForm
from core.lib import market
from core.lib.schemas.drakdoo_s import DrakdooCallback from core.lib.schemas.drakdoo_s import DrakdooCallback
from core.models import Callback, Hook, Signal from core.models import Callback, Hook, Signal
from core.trading import market
from core.util import logs from core.util import logs
from core.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate from core.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate