I think you can do this with two red-black trees: a key search tree for storing keys ordered by the comparison function, and an index search tree with keys in random order, as in a list. Each node index search must have a "size" field - the Red-Black tree can search by index if the "size" field is included in each node. See, for example, the implementation of RedBlackTreeSet in the C5 Generic Collection Library .
Each entry in the key search tree requires a pointer to its corresponding entry in the index-lookup tree. Like the left and right-node pointers, the index search tree will require a parent pointer field to provide top-down navigation as well as top-down navigation.
For each key, six pointers are required: regular left and right pointers in both nodes, as well as a pointer from the-lookup-node to index-lookup-node, plus the parent pointer in each index-lookup node. You will also need a pointer in each node to indicate the stored value.
Operations:
Append - the add operation inserts a key into both trees - once in the key search tree at the position determined by the comparison function, and again in the far right direction of the index search tree. Insertion into a red-black tree is a logarithmic operation of time.
Key search - this is done in the key search tree using the comparison function to find the correct position - O (log (n))
Index search - this can be done in the index search field, as described above - O (log (n))
Get the index from the key - first find the key in the key search tree O (log (n)). Follow the pointer to the index search tree. Follow the parent's pointers to the root of the node, (O (log (n)) for a balanced tree). Use the size fields on the way up to determine the key index. - O (log (n)) as a whole.
Delete by index - search for an item in the index tree. Remove from index search tree. Find the key located in the key search tree. Remove from the key search tree. All operations: O (log (n)), therefore delete - O (log (n)) as a whole.
Delete key - use "Get Index from Key" to get the key index. Delete by index from the index search tree. Delete a key from the key search tree. O (log (n)) as a whole.
This structure also supports setting O (log (n)) at any arbitrary position, not just at the end.
The storage overhead is clearly significant, but O (n) remains. The complexity of the time meets all the requirements.
Unfortunately, I do not know about any implementation of this structure.
Update. It seems to me that you can combine a tree with a hash table to get a search on O (1). Instead of having two trees, as I suggest above, use a hash table for searching by key and a balanced statistics tree in order to search by position as above, but have hash table slots containing pointers to nodes of the balanced tree to search get-list-position-by-key. Currently, the search is by keywords O (1), while everything else remains O (ln (n)) on average. Of course, now you get a random O (n) re-hash penalty, as in any hash table.